<commit_before>package httpclient

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"reflect"

	"github.com/gobs/pretty"
	"github.com/gobs/simplejson"
)

type HttpResponse struct {
	http.Response
}

//
// Check if the input value is a "primitive" that can be safely stringified
//
func canStringify(v reflect.Value) bool {
	switch v.Kind() {
	case reflect.Bool,
		reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
		reflect.Float32, reflect.Float64,
		reflect.String:
		return true
	default:
		return false
	}
}

//
// Given a base URL and a bag of parameteters returns the URL with the encoded parameters
//
func URLWithPathParams(base string, path string, params map[string]interface{}) (u *url.URL) {

	u, err := url.Parse(base)
	if err != nil {
		log.Fatal(err)
	}

	if len(path) > 0 {
		u, err = u.Parse(path)
		if err != nil {
			log.Fatal(err)
		}
	}

	q := u.Query()

	for k, v := range params {
		val := reflect.ValueOf(v)

		switch val.Kind() {
		case reflect.Slice:
			if val.IsNil() { // TODO: add an option to ignore empty values
				q.Set(k, "")
				continue
			}
			fallthrough

		case reflect.Array:
			for i := 0; i < val.Len(); i++ {
				av := val.Index(i)

				if canStringify(av) {
					q.Add(k, fmt.Sprintf("%v", av))
				}
			}

		default:
			if canStringify(val) {
				q.Set(k, fmt.Sprintf("%v", v))
			} else {
				log.Fatal("Invalid type ", val)
			}
		}
	}

	u.RawQuery = q.Encode()
	return u
}

func URLWithParams(base string, params map[string]interface{}) (u *url.URL) {
	return URLWithPathParams(base, "", params)
}

//
// http.Get with params
//
func Get(urlStr string, params map[string]interface{}) (*HttpResponse, error) {
	resp, err := http.Get(URLWithParams(urlStr, params).String())
	if err == nil {
		return &HttpResponse{*resp}, nil
	} else {
		return nil, err
	}
}

//
// http.Post with params
//
func Post(urlStr string, params map[string]interface{}) (*HttpResponse, error) {
	resp, err := http.PostForm(urlStr, URLWithParams(urlStr, params).Query())
	if err == nil {
		return &HttpResponse{*resp}, nil
	} else {
		return nil, err
	}
}

//
//  Read the body
//
func (resp *HttpResponse) Content() []byte {
	body, _ := ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	return body
}

//
//  Try to parse the response body as JSON
//
func (resp *HttpResponse) Json() (json *simplejson.Json) {
	json, _ = simplejson.LoadBytes(resp.Content())
	return
}

////////////////////////////////////////////////////////////////////////

//
// http.Client with some defaults and stuff
//
type HttpClient struct {
	// the http.Client
	client *http.Client

	// the base URL for this client
	BaseURL *url.URL

	// the client UserAgent string
	UserAgent string

	// Common headers to be passed on each request
	Headers map[string]string

	// if Verbose, log request and response info
	Verbose bool
}

//
// Create a new HttpClient
//
func NewHttpClient(base string) (httpClient *HttpClient) {
	httpClient = new(HttpClient)
	httpClient.client = &http.Client{CheckRedirect: httpClient.checkRedirect}
	httpClient.Headers = make(map[string]string)

	if u, err := url.Parse(base); err != nil {
		log.Fatal(err)
	} else {
		httpClient.BaseURL = u
	}

	return
}

//
// add default headers plus extra headers
//
func (self *HttpClient) addHeaders(req *http.Request, headers map[string]string) {

	if len(self.UserAgent) > 0 {
		req.Header.Set("User-Agent", self.UserAgent)
	}

	for k, v := range self.Headers {
		req.Header.Set(k, v)
	}

	for k, v := range headers {
		req.Header.Set(k, v)
	}
}

//
// the callback for CheckRedirect, used to pass along the headers in case of redirection
//
func (self *HttpClient) checkRedirect(req *http.Request, via []*http.Request) error {
	if self.Verbose {
		log.Println("REDIRECT:", len(via), req.URL)
	}

	if len(via) >= 10 {
		return errors.New("stopped after 10 redirects")
	}

	// TODO: check for same host before adding headers
	self.addHeaders(req, nil)
	return nil
}

//
// Create a request object given the method, path, body and extra headers
//
func (self *HttpClient) Request(method string, urlpath string, body io.Reader, headers map[string]string) (req *http.Request) {
	if u, err := self.BaseURL.Parse(urlpath); err != nil {
		log.Fatal(err)
	} else {
		urlpath = u.String()
	}

	req, err := http.NewRequest(method, urlpath, body)
	if err != nil {
		log.Fatal(err)
	}

	self.addHeaders(req, headers)
	return
}

//
// Execute request
//
func (self *HttpClient) Do(req *http.Request) (*HttpResponse, error) {
	if self.Verbose {
		log.Println("REQUEST:", req.Method, req.URL, pretty.PrettyFormat(req.Header))
	}

	resp, err := self.client.Do(req)
	if err == nil {
		if self.Verbose {
			log.Println("RESPONSE:", resp.Status, pretty.PrettyFormat(resp.Header))
		}

		return &HttpResponse{*resp}, nil
	} else {
		if self.Verbose {
			log.Println("ERROR:", err)
		}

		return nil, err
	}
}

//
// Execute a DELETE request
//
func (self *HttpClient) Delete(path string, headers map[string]string) (*HttpResponse, error) {
	req := self.Request("DELETE", path, nil, headers)
	return self.Do(req)
}

//
// Execute a HEAD request
//
func (self *HttpClient) Head(path string, params map[string]interface{}, headers map[string]string) (*HttpResponse, error) {
	req := self.Request("HEAD", URLWithParams(path, params).String(), nil, headers)
	return self.Do(req)
}

//
// Execute a GET request
//
func (self *HttpClient) Get(path string, params map[string]interface{}, headers map[string]string) (*HttpResponse, error) {
	req := self.Request("GET", URLWithParams(path, params).String(), nil, headers)
	return self.Do(req)
}

//
// Execute a POST request
//
func (self *HttpClient) Post(path string, content io.Reader, headers map[string]string) (*HttpResponse, error) {
	req := self.Request("POST", path, content, headers)
	if headers != nil {
		for k, v := range headers {
			req.Header.Set(k, v)
		}
	}
	return self.Do(req)
}
<commit_msg>Added HttpResponse.ContentType(), so clients can avoid having to fetch and parse the Content-Type header<commit_after>package httpclient

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"reflect"
        "strings"

	"github.com/gobs/pretty"
	"github.com/gobs/simplejson"
)

type HttpResponse struct {
	http.Response
}

func (r *HttpResponse) ContentType() string {
    content_type := r.Header.Get("Content-Type")
    if len(content_type) == 0 {
        return content_type
    }

    return strings.TrimSpace(strings.Split(content_type, ";")[0])
}

//
// Check if the input value is a "primitive" that can be safely stringified
//
func canStringify(v reflect.Value) bool {
	switch v.Kind() {
	case reflect.Bool,
		reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
		reflect.Float32, reflect.Float64,
		reflect.String:
		return true
	default:
		return false
	}
}

//
// Given a base URL and a bag of parameteters returns the URL with the encoded parameters
//
func URLWithPathParams(base string, path string, params map[string]interface{}) (u *url.URL) {

	u, err := url.Parse(base)
	if err != nil {
		log.Fatal(err)
	}

	if len(path) > 0 {
		u, err = u.Parse(path)
		if err != nil {
			log.Fatal(err)
		}
	}

	q := u.Query()

	for k, v := range params {
		val := reflect.ValueOf(v)

		switch val.Kind() {
		case reflect.Slice:
			if val.IsNil() { // TODO: add an option to ignore empty values
				q.Set(k, "")
				continue
			}
			fallthrough

		case reflect.Array:
			for i := 0; i < val.Len(); i++ {
				av := val.Index(i)

				if canStringify(av) {
					q.Add(k, fmt.Sprintf("%v", av))
				}
			}

		default:
			if canStringify(val) {
				q.Set(k, fmt.Sprintf("%v", v))
			} else {
				log.Fatal("Invalid type ", val)
			}
		}
	}

	u.RawQuery = q.Encode()
	return u
}

func URLWithParams(base string, params map[string]interface{}) (u *url.URL) {
	return URLWithPathParams(base, "", params)
}

//
// http.Get with params
//
func Get(urlStr string, params map[string]interface{}) (*HttpResponse, error) {
	resp, err := http.Get(URLWithParams(urlStr, params).String())
	if err == nil {
		return &HttpResponse{*resp}, nil
	} else {
		return nil, err
	}
}

//
// http.Post with params
//
func Post(urlStr string, params map[string]interface{}) (*HttpResponse, error) {
	resp, err := http.PostForm(urlStr, URLWithParams(urlStr, params).Query())
	if err == nil {
		return &HttpResponse{*resp}, nil
	} else {
		return nil, err
	}
}

//
//  Read the body
//
func (resp *HttpResponse) Content() []byte {
	body, _ := ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	return body
}

//
//  Try to parse the response body as JSON
//
func (resp *HttpResponse) Json() (json *simplejson.Json) {
	json, _ = simplejson.LoadBytes(resp.Content())
	return
}

////////////////////////////////////////////////////////////////////////

//
// http.Client with some defaults and stuff
//
type HttpClient struct {
	// the http.Client
	client *http.Client

	// the base URL for this client
	BaseURL *url.URL

	// the client UserAgent string
	UserAgent string

	// Common headers to be passed on each request
	Headers map[string]string

	// if Verbose, log request and response info
	Verbose bool
}

//
// Create a new HttpClient
//
func NewHttpClient(base string) (httpClient *HttpClient) {
	httpClient = new(HttpClient)
	httpClient.client = &http.Client{CheckRedirect: httpClient.checkRedirect}
	httpClient.Headers = make(map[string]string)

	if u, err := url.Parse(base); err != nil {
		log.Fatal(err)
	} else {
		httpClient.BaseURL = u
	}

	return
}

//
// add default headers plus extra headers
//
func (self *HttpClient) addHeaders(req *http.Request, headers map[string]string) {

	if len(self.UserAgent) > 0 {
		req.Header.Set("User-Agent", self.UserAgent)
	}

	for k, v := range self.Headers {
		req.Header.Set(k, v)
	}

	for k, v := range headers {
		req.Header.Set(k, v)
	}
}

//
// the callback for CheckRedirect, used to pass along the headers in case of redirection
//
func (self *HttpClient) checkRedirect(req *http.Request, via []*http.Request) error {
	if self.Verbose {
		log.Println("REDIRECT:", len(via), req.URL)
	}

	if len(via) >= 10 {
		return errors.New("stopped after 10 redirects")
	}

	// TODO: check for same host before adding headers
	self.addHeaders(req, nil)
	return nil
}

//
// Create a request object given the method, path, body and extra headers
//
func (self *HttpClient) Request(method string, urlpath string, body io.Reader, headers map[string]string) (req *http.Request) {
	if u, err := self.BaseURL.Parse(urlpath); err != nil {
		log.Fatal(err)
	} else {
		urlpath = u.String()
	}

	req, err := http.NewRequest(method, urlpath, body)
	if err != nil {
		log.Fatal(err)
	}

	self.addHeaders(req, headers)
	return
}

//
// Execute request
//
func (self *HttpClient) Do(req *http.Request) (*HttpResponse, error) {
	if self.Verbose {
		log.Println("REQUEST:", req.Method, req.URL, pretty.PrettyFormat(req.Header))
	}

	resp, err := self.client.Do(req)
	if err == nil {
		if self.Verbose {
			log.Println("RESPONSE:", resp.Status, pretty.PrettyFormat(resp.Header))
		}

		return &HttpResponse{*resp}, nil
	} else {
		if self.Verbose {
			log.Println("ERROR:", err)
		}

		return nil, err
	}
}

//
// Execute a DELETE request
//
func (self *HttpClient) Delete(path string, headers map[string]string) (*HttpResponse, error) {
	req := self.Request("DELETE", path, nil, headers)
	return self.Do(req)
}

//
// Execute a HEAD request
//
func (self *HttpClient) Head(path string, params map[string]interface{}, headers map[string]string) (*HttpResponse, error) {
	req := self.Request("HEAD", URLWithParams(path, params).String(), nil, headers)
	return self.Do(req)
}

//
// Execute a GET request
//
func (self *HttpClient) Get(path string, params map[string]interface{}, headers map[string]string) (*HttpResponse, error) {
	req := self.Request("GET", URLWithParams(path, params).String(), nil, headers)
	return self.Do(req)
}

//
// Execute a POST request
//
func (self *HttpClient) Post(path string, content io.Reader, headers map[string]string) (*HttpResponse, error) {
	req := self.Request("POST", path, content, headers)
	if headers != nil {
		for k, v := range headers {
			req.Header.Set(k, v)
		}
	}
	return self.Do(req)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"
	"net/url"
	"io"
	"io/ioutil"
	"strings"
	"bytes"
	"encoding/json"
	"regexp"
)

const CONFIG_FILE string = "../config/client2.json"

// A global configuration instance. Must be instantiated properly in main().
var Configuration Config

const URL_REGEX = "(https?://)?(www\\.)?\\w+\\.\\w+"

// Result of a bundle lookup from cache server.
type Result struct {
	Complete bool
	Found    bool
	Bundle   []byte
	// Should add a Created field for the date created
}

// Present the user with a page informing them that something went wrong with
// an action.
func errorPage(errMsg string) []byte {
	return []byte("Error: " + errMsg)
}

// Serve a page to inform the user that a bundle for the site they requested is
// being prepared. It will automatically initiate new requests to retrieve the same
// URL in an interval.
func pleaseWait(url string) []byte {
	content, _ := ioutil.ReadFile(Configuration.PleaseWaitPage)
	return bytes.Replace(content, []byte("{{REDIRECT}}"), []byte(url), 1)
}

// Ping the LCS to see if it is available at a given time.
func testLCSAvailability() bool {
	response, err := http.Get(LCSPingURL(Configuration))
	return err == nil && response.StatusCode == 200
}

// Ping the RS to see if it is available at a given time.
func testRSAvailability() bool {
	response, err := http.Get(RSPingURL(Configuration))
	return err == nil && response.StatusCode == 200
}

// Report that an error occured trying to decode the response from the LCS
// The LCS is expected to respond to this request with just the string "okay",
// so we will ignore it for now.
func reportDecodeError(reportURL, errMSg string) (bool, error) {
	jsonStr := "{\"error\": \"" + errMsg + "\"}"
	response, err := http.Post(reportURL, "application/json", strings.NewReader(jsonStr))
	defer response.Body.Close()
	return response.StatusCode == 200, err
}

// Check with the local cache server to find a bundle for a given URL.
func lookup(lookupURL string) (Result, err) {
	response, err := http.Get(BundleLookupURL(Configuration, lookupURL))
	if err != nil || response.StatusCode != 200 {
		fmt.Print("error: ")
		fmt.Println(err)
		return Result{false, false, nil}, errors.New("Unsuccessful request to LCS\n" + err.Error())
	}
	decoder := json.NewDecoder(response.Body)
	var result Result
	if err := decoder.Decode(&result); err == io.EOF {
		fmt.Println("Error decoding result; Error: ")
		fmt.Println(err)
		reachedLCS, err2 := reportDecodeError(DecodeErrReportURL(Configuration), err.Error())
		if reachedLCS {
			return Result{false, false, nil}, errors.New("Could not decode LCS response\n" + err.Error())
		} else {
			return Result{false, false, nil}, errors.New("Unsuccessful request to LCS\n" + err2.Error())
		}
	}
	fmt.Println("Result")
	fmt.Println(result)
	return result, nil
}

// POST to the request server to have it start making a new bundle.
func requestNewBundle(lookupURL string) err {
	// We can ignore the content of the response since it is not used.
	response, err := http.Post(
		CreateBundleURL(Configuration, lookupURL),
		"text/plain",
		strings.NewReader(lookupURL))
	fmt.Println("Sent POST request to Request Server")
	if err != nil || response.StatusCode != 200 {
		fmt.Println("Got error POSTing to request server or request did not return status 200")
		fmt.Println(err)
	} else {
		response.Body.Close()
	}
	return err
}

// Handle incoming requests for bundles.
// 1. Initiate bundle lookup process
// 2. Initiate bundle creation process when no bundle exists anywhere
func proxyHandler(w http.ResponseWriter, r *http.Request) {
	URL := r.URL.String()
	matched, err := regexp.MatchString(URL_REGEX, URL)
	if !matched || err != nil {
		fmt.Println("Invalid URL " + URL)
		w.Write(errorPage(URL + " is not a valid URL."))
		return
	}
	result, err := lookup(URL)
	if err != nil {
		w.Write(errorPage(err.Error()))
	} else if result.Complete {
		if result.Found {
			w.Write(result.Bundle)
		} else {
			err = requestNewBundle(URL)
			if err != nil {
				w.Write(errorPage(err.Error()))
			} else {
				w.Write(pleaseWait(URL))
			}
		}
	} else {
		w.Write(pleaseWait(URL))
	}
}

func main() {
	// Read an existing configuration file or have the user supply settings
	conf, err := ReadConfigFile(CONFIG_FILE)
	if err != nil {
		fmt.Print("Could not read configuration file at " + CONFIG_FILE)
		Configuration = GetConfigFromUser()
	} else {
		Configuration = conf
	}
	// Ensure the LCS is available at startup time
	available := testLCSAvailability()
	if !available {
		fmt.Println("Local cache server is not responding to requests.")
		fmt.Println(LCS_RUN_INFO)
		return
	}
	// Ensure the RS is available at startup time
	available := testRSAvailability()
	if !available {
		fmt.Println("Request server is not responding to requests.")
		fmt.Println(RS_RUN_INFO)
		return
	}
	// Create an HTTP proxy server
	http.HandleFunc("/", proxyHandler)
	fmt.Println("CeNo proxy server listening at http://localhost" + Configuration.PortNumber)
	http.ListenAndServe(Configuration.PortNumber, nil)
}<commit_msg>If no view is available to serve the please wait page, then serve a plaintext response instead.<commit_after>package main

import (
	"fmt"
	"net/http"
	"net/url"
	"io"
	"io/ioutil"
	"strings"
	"bytes"
	"encoding/json"
	"regexp"
)

const CONFIG_FILE string = "../config/client2.json"

// A global configuration instance. Must be instantiated properly in main().
var Configuration Config

// Verifies a URL as valid (enough)
const URL_REGEX = "(https?://)?(www\\.)?\\w+\\.\\w+"

// In the case that wait.html cannot be served, we will respond with a
// plain text message to the user.
const PLEASE_WAIT_PLAINTEXT = /* Multi-line strings, yeah! */ `
The page you have requested is being prepared.
Plesae refresh this page in a few seconds to check if it is ready.
`

// Result of a bundle lookup from cache server.
type Result struct {
	Complete bool
	Found    bool
	Bundle   []byte
	// Should add a Created field for the date created
}

// Present the user with a page informing them that something went wrong with
// an action.
func errorPage(errMsg string) []byte {
	return []byte("Error: " + errMsg)
}

// Serve a page to inform the user that a bundle for the site they requested is
// being prepared. It will automatically initiate new requests to retrieve the same
// URL in an interval.
// The second bool return value specifies whether the response is HTML or not
func pleaseWait(url string) ([]byte, bool) {
	content, err := ioutil.ReadFile(Configuration.PleaseWaitPage)
	if err != nil {
		return PLEASE_WAIT_PLAINTEXT, false
	} else {
		return bytes.Replace(content, []byte("{{REDIRECT}}"), []byte(url), 1), true
	}
}

// Ping the LCS to see if it is available at a given time.
func testLCSAvailability() bool {
	response, err := http.Get(LCSPingURL(Configuration))
	return err == nil && response.StatusCode == 200
}

// Ping the RS to see if it is available at a given time.
func testRSAvailability() bool {
	response, err := http.Get(RSPingURL(Configuration))
	return err == nil && response.StatusCode == 200
}

// Report that an error occured trying to decode the response from the LCS
// The LCS is expected to respond to this request with just the string "okay",
// so we will ignore it for now.
func reportDecodeError(reportURL, errMSg string) (bool, error) {
	jsonStr := "{\"error\": \"" + errMsg + "\"}"
	response, err := http.Post(reportURL, "application/json", strings.NewReader(jsonStr))
	defer response.Body.Close()
	return response.StatusCode == 200, err
}

// Check with the local cache server to find a bundle for a given URL.
func lookup(lookupURL string) (Result, err) {
	response, err := http.Get(BundleLookupURL(Configuration, lookupURL))
	if err != nil || response.StatusCode != 200 {
		fmt.Print("error: ")
		fmt.Println(err)
		return Result{false, false, nil}, errors.New("Unsuccessful request to LCS\n" + err.Error())
	}
	decoder := json.NewDecoder(response.Body)
	var result Result
	if err := decoder.Decode(&result); err == io.EOF {
		fmt.Println("Error decoding result; Error: ")
		fmt.Println(err)
		reachedLCS, err2 := reportDecodeError(DecodeErrReportURL(Configuration), err.Error())
		if reachedLCS {
			return Result{false, false, nil}, errors.New("Could not decode LCS response\n" + err.Error())
		} else {
			return Result{false, false, nil}, errors.New("Unsuccessful request to LCS\n" + err2.Error())
		}
	}
	fmt.Println("Result")
	fmt.Println(result)
	return result, nil
}

// POST to the request server to have it start making a new bundle.
func requestNewBundle(lookupURL string) err {
	// We can ignore the content of the response since it is not used.
	response, err := http.Post(
		CreateBundleURL(Configuration, lookupURL),
		"text/plain",
		strings.NewReader(lookupURL))
	fmt.Println("Sent POST request to Request Server")
	if err != nil || response.StatusCode != 200 {
		fmt.Println("Got error POSTing to request server or request did not return status 200")
		fmt.Println(err)
	} else {
		response.Body.Close()
	}
	return err
}

// Handle incoming requests for bundles.
// 1. Initiate bundle lookup process
// 2. Initiate bundle creation process when no bundle exists anywhere
func proxyHandler(w http.ResponseWriter, r *http.Request) {
	URL := r.URL.String()
	matched, err := regexp.MatchString(URL_REGEX, URL)
	if !matched || err != nil {
		fmt.Println("Invalid URL " + URL)
		w.Write(errorPage(URL + " is not a valid URL."))
		return
	}
	result, err := lookup(URL)
	if err != nil {
		w.Write(errorPage(err.Error()))
	} else if result.Complete {
		if result.Found {
			w.Write(result.Bundle)
		} else {
			err = requestNewBundle(URL)
			if err != nil {
				w.Header().Set("Content-Type", "text/plain")
				w.Write(errorPage(err.Error()))
			} else {
				body, isHTML := pleaseWait(URL)
				if isHTML {
					w.Header().Set("Content-Type", "text/html")
				} else {
					w.Header().Set("Content-Type", "text/plain")
				}
				w.Write(body)
			}
		}
	} else {
		body, isHTML := pleaseWait(URL)
		if isHTML {
			w.Header().Set("Content-Type", "text/html")
		} else {
			w.Header().Set("Content-Type", "text/plain")
		}
		w.Write(body)
	}
}

func main() {
	// Read an existing configuration file or have the user supply settings
	conf, err := ReadConfigFile(CONFIG_FILE)
	if err != nil {
		fmt.Print("Could not read configuration file at " + CONFIG_FILE)
		Configuration = GetConfigFromUser()
	} else {
		Configuration = conf
	}
	// Ensure the LCS is available at startup time
	available := testLCSAvailability()
	if !available {
		fmt.Println("Local cache server is not responding to requests.")
		fmt.Println(LCS_RUN_INFO)
		return
	}
	// Ensure the RS is available at startup time
	available := testRSAvailability()
	if !available {
		fmt.Println("Request server is not responding to requests.")
		fmt.Println(RS_RUN_INFO)
		return
	}
	// Create an HTTP proxy server
	http.HandleFunc("/", proxyHandler)
	fmt.Println("CeNo proxy server listening at http://localhost" + Configuration.PortNumber)
	http.ListenAndServe(Configuration.PortNumber, nil)
}<|endoftext|>


<commit_before>// Copyright 2016, RadiantBlue Technologies, Inc.
//
// 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 workflow

import (
	"net/http"

	"github.com/venicegeo/pz-gocommon/gocommon"
	logger "github.com/venicegeo/pz-logger/logger"
)

type Client struct {
	url    string
	logger logger.IClient
}

func NewClient(sys *piazza.SystemConfig, logger logger.IClient) (*Client, error) {

	var err error

	err = sys.WaitForService(piazza.PzWorkflow)
	if err != nil {
		return nil, err
	}

	url, err := sys.GetURL(piazza.PzWorkflow)

	service := &Client{
		url:    url,
		logger: logger,
	}

	service.logger.Info("Client started")

	return service, nil
}

//------------------------------------------------------------------------------

func asEventType(resp *piazza.JsonResponse) (*EventType, error) {
	var et EventType

	err := resp.ExtractData(&et)
	if err != nil {
		return nil, err
	}

	return &et, nil
}

func asEventTypeArray(resp *piazza.JsonResponse) (*[]EventType, error) {
	var ets []EventType

	err := resp.ExtractData(&ets)
	if err != nil {
		return nil, err
	}

	return &ets, nil
}

//------------------------------------------------------------------------------

func (c *Client) getObject(endpoint string, out interface{}) error {
	//log.Printf("** ** ** %s", reflect.TypeOf(out).String())

	h := piazza.Http{BaseUrl: c.url}
	resp := h.PzGet(endpoint)
	//log.Printf("** ** ** %s", reflect.TypeOf(resp.Data).String())
	if resp.IsError() {
		return resp.ToError()
	}
	if resp.StatusCode != http.StatusOK {
		return resp.ToError()
	}
	//log.Printf("getObject/1 %#v", out)
	err := resp.ExtractData(out)
	//log.Printf("getObject/2 %#v", out)
	if err != nil {
		return err
	}
	return nil
}

func (c *Client) postObject(obj interface{}, endpoint string, out interface{}) error {
	h := piazza.Http{BaseUrl: c.url}
	resp := h.PzPost(endpoint, obj)
	if resp.IsError() {
		return resp.ToError()
	}
	if resp.StatusCode != http.StatusCreated {
		return resp.ToError()
	}
	err := resp.ExtractData(out)
	if err != nil {
		return err
	}
	return nil
}

func (c *Client) putObject(obj interface{}, endpoint string, out interface{}) error {
	h := piazza.Http{BaseUrl: c.url}
	resp := h.PzPut(endpoint, obj)
	if resp.IsError() {
		return resp.ToError()
	}
	if resp.StatusCode != http.StatusOK {
		return resp.ToError()
	}

	err := resp.ExtractData(out)
	if err != nil {
		return err
	}

	return nil
}

func (c *Client) deleteObject(endpoint string) error {
	h := piazza.Http{BaseUrl: c.url}
	resp := h.PzDelete(endpoint)
	if resp.IsError() {
		return resp.ToError()
	}
	if resp.StatusCode != http.StatusOK {
		return resp.ToError()
	}

	return nil
}

//------------------------------------------------------------------------------

func (c *Client) GetEventType(id piazza.Ident) (*EventType, error) {
	out := &EventType{}
	err := c.getObject("/eventType/"+id.String(), out)
	return out, err
}

func (c *Client) GetAllEventTypes() (*[]EventType, error) {
	out := &[]EventType{}
	err := c.getObject("/eventType", out)
	return out, err
}

func (c *Client) PostEventType(eventType *EventType) (*EventType, error) {
	out := &EventType{}
	err := c.postObject(eventType, "/eventType", out)
	return out, err
}

func (c *Client) PutEventType(eventType *EventType) (*EventType, error) {
	out := &EventType{}
	err := c.putObject(eventType, "/eventType", out)
	return out, err
}

func (c *Client) DeleteEventType(id piazza.Ident) error {
	err := c.deleteObject("/eventType/" + id.String())
	return err
}

//------------------------------------------------------------------------------

func (c *Client) GetEvent(id piazza.Ident) (*Event, error) {
	out := &Event{}
	err := c.getObject("/event/"+id.String(), out)
	return out, err
}

func (c *Client) GetAllEvents() (*[]Event, error) {
	out := &[]Event{}
	err := c.getObject("/event", out)
	return out, err
}

func (c *Client) GetAllEventsByEventType(eventTypeId piazza.Ident) (*[]Event, error) {
	out := &[]Event{}
	err := c.getObject("/event?eventTypeId="+eventTypeId.String(), out)
	return out, err
}

func (c *Client) PostEvent(event *Event) (*Event, error) {
	out := &Event{}
	err := c.postObject(event, "/event", out)
	return out, err
}

func (c *Client) PutEvent(event *Event) (*Event, error) {
	out := &Event{}
	err := c.putObject(event, "/event", out)
	return out, err
}

func (c *Client) DeleteEvent(id piazza.Ident) error {
	err := c.deleteObject("/event/" + id.String())
	return err
}

//------------------------------------------------------------------------------

func (c *Client) GetTrigger(id piazza.Ident) (*Trigger, error) {
	out := &Trigger{}
	err := c.getObject("/trigger/"+id.String(), out)
	return out, err
}

func (c *Client) GetAllTriggers() ([]Trigger, error) {
	out := []Trigger{}
	err := c.getObject("/trigger", &out)
	return out, err
}

func (c *Client) PostTrigger(trigger *Trigger) (*Trigger, error) {
	out := &Trigger{}
	err := c.postObject(trigger, "/trigger", out)
	return out, err
}

func (c *Client) PutTrigger(trigger *Trigger) (*Trigger, error) {
	out := &Trigger{}
	err := c.putObject(trigger, "/trigger", out)
	return out, err
}

func (c *Client) DeleteTrigger(id piazza.Ident) error {
	err := c.deleteObject("/trigger/" + id.String())
	return err
}

//------------------------------------------------------------------------------

func (c *Client) GetAlert(id piazza.Ident) (*Alert, error) {
	out := &Alert{}
	err := c.getObject("/alert/"+id.String(), out)
	return out, err
}

func (c *Client) GetAllAlerts() (*[]Alert, error) {
	out := &[]Alert{}
	err := c.getObject("/alert", out)
	return out, err
}

func (c *Client) PostAlert(alert *Alert) (*Alert, error) {
	out := &Alert{}
	err := c.postObject(alert, "/alert", out)
	return out, err
}

func (c *Client) PutAlert(alert *Alert) (*Alert, error) {
	out := &Alert{}
	err := c.putObject(alert, "/alert", out)
	return out, err
}

func (c *Client) DeleteAlert(id piazza.Ident) error {
	err := c.deleteObject("/alert/" + id.String())
	return err
}

//------------------------------------------------------------------------------

func (c *Client) GetStats() (*workflowAdminStats, error) {
	out := &workflowAdminStats{}
	err := c.getObject("/admin/stats", out)
	return out, err

}
<commit_msg>added NewClient2()<commit_after>// Copyright 2016, RadiantBlue Technologies, Inc.
//
// 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 workflow

import (
	"net/http"
	"strings"

	"github.com/venicegeo/pz-gocommon/gocommon"
	loggerpkg "github.com/venicegeo/pz-logger/logger"
)

type Client struct {
	url    string
	logger loggerpkg.IClient
}

func NewClient(sys *piazza.SystemConfig, logger loggerpkg.IClient) (*Client, error) {

	var err error

	err = sys.WaitForService(piazza.PzWorkflow)
	if err != nil {
		return nil, err
	}

	url, err := sys.GetURL(piazza.PzWorkflow)

	service := &Client{
		url:    url,
		logger: logger,
	}

	service.logger.Info("Client started")

	return service, nil
}

func NewClient2(url string, apiKey string) (*Client, error) {

	var err error

	loggerUrl := strings.Replace(url, "workflow", "logger", 1)
	logger, err := loggerpkg.NewClient2(loggerUrl, apiKey)
	if err != nil {
		return nil, err
	}

	service := &Client{
		url:    url,
		logger: logger,
	}

	return service, nil
}

//------------------------------------------------------------------------------

func asEventType(resp *piazza.JsonResponse) (*EventType, error) {
	var et EventType

	err := resp.ExtractData(&et)
	if err != nil {
		return nil, err
	}

	return &et, nil
}

func asEventTypeArray(resp *piazza.JsonResponse) (*[]EventType, error) {
	var ets []EventType

	err := resp.ExtractData(&ets)
	if err != nil {
		return nil, err
	}

	return &ets, nil
}

//------------------------------------------------------------------------------

func (c *Client) getObject(endpoint string, out interface{}) error {
	//log.Printf("** ** ** %s", reflect.TypeOf(out).String())

	h := piazza.Http{BaseUrl: c.url}
	resp := h.PzGet(endpoint)
	//log.Printf("** ** ** %s", reflect.TypeOf(resp.Data).String())
	if resp.IsError() {
		return resp.ToError()
	}
	if resp.StatusCode != http.StatusOK {
		return resp.ToError()
	}
	//log.Printf("getObject/1 %#v", out)
	err := resp.ExtractData(out)
	//log.Printf("getObject/2 %#v", out)
	if err != nil {
		return err
	}
	return nil
}

func (c *Client) postObject(obj interface{}, endpoint string, out interface{}) error {
	h := piazza.Http{BaseUrl: c.url}
	resp := h.PzPost(endpoint, obj)
	if resp.IsError() {
		return resp.ToError()
	}
	if resp.StatusCode != http.StatusCreated {
		return resp.ToError()
	}
	err := resp.ExtractData(out)
	if err != nil {
		return err
	}
	return nil
}

func (c *Client) putObject(obj interface{}, endpoint string, out interface{}) error {
	h := piazza.Http{BaseUrl: c.url}
	resp := h.PzPut(endpoint, obj)
	if resp.IsError() {
		return resp.ToError()
	}
	if resp.StatusCode != http.StatusOK {
		return resp.ToError()
	}

	err := resp.ExtractData(out)
	if err != nil {
		return err
	}

	return nil
}

func (c *Client) deleteObject(endpoint string) error {
	h := piazza.Http{BaseUrl: c.url}
	resp := h.PzDelete(endpoint)
	if resp.IsError() {
		return resp.ToError()
	}
	if resp.StatusCode != http.StatusOK {
		return resp.ToError()
	}

	return nil
}

//------------------------------------------------------------------------------

func (c *Client) GetEventType(id piazza.Ident) (*EventType, error) {
	out := &EventType{}
	err := c.getObject("/eventType/"+id.String(), out)
	return out, err
}

func (c *Client) GetAllEventTypes() (*[]EventType, error) {
	out := &[]EventType{}
	err := c.getObject("/eventType", out)
	return out, err
}

func (c *Client) PostEventType(eventType *EventType) (*EventType, error) {
	out := &EventType{}
	err := c.postObject(eventType, "/eventType", out)
	return out, err
}

func (c *Client) PutEventType(eventType *EventType) (*EventType, error) {
	out := &EventType{}
	err := c.putObject(eventType, "/eventType", out)
	return out, err
}

func (c *Client) DeleteEventType(id piazza.Ident) error {
	err := c.deleteObject("/eventType/" + id.String())
	return err
}

//------------------------------------------------------------------------------

func (c *Client) GetEvent(id piazza.Ident) (*Event, error) {
	out := &Event{}
	err := c.getObject("/event/"+id.String(), out)
	return out, err
}

func (c *Client) GetAllEvents() (*[]Event, error) {
	out := &[]Event{}
	err := c.getObject("/event", out)
	return out, err
}

func (c *Client) GetAllEventsByEventType(eventTypeId piazza.Ident) (*[]Event, error) {
	out := &[]Event{}
	err := c.getObject("/event?eventTypeId="+eventTypeId.String(), out)
	return out, err
}

func (c *Client) PostEvent(event *Event) (*Event, error) {
	out := &Event{}
	err := c.postObject(event, "/event", out)
	return out, err
}

func (c *Client) PutEvent(event *Event) (*Event, error) {
	out := &Event{}
	err := c.putObject(event, "/event", out)
	return out, err
}

func (c *Client) DeleteEvent(id piazza.Ident) error {
	err := c.deleteObject("/event/" + id.String())
	return err
}

//------------------------------------------------------------------------------

func (c *Client) GetTrigger(id piazza.Ident) (*Trigger, error) {
	out := &Trigger{}
	err := c.getObject("/trigger/"+id.String(), out)
	return out, err
}

func (c *Client) GetAllTriggers() ([]Trigger, error) {
	out := []Trigger{}
	err := c.getObject("/trigger", &out)
	return out, err
}

func (c *Client) PostTrigger(trigger *Trigger) (*Trigger, error) {
	out := &Trigger{}
	err := c.postObject(trigger, "/trigger", out)
	return out, err
}

func (c *Client) PutTrigger(trigger *Trigger) (*Trigger, error) {
	out := &Trigger{}
	err := c.putObject(trigger, "/trigger", out)
	return out, err
}

func (c *Client) DeleteTrigger(id piazza.Ident) error {
	err := c.deleteObject("/trigger/" + id.String())
	return err
}

//------------------------------------------------------------------------------

func (c *Client) GetAlert(id piazza.Ident) (*Alert, error) {
	out := &Alert{}
	err := c.getObject("/alert/"+id.String(), out)
	return out, err
}

func (c *Client) GetAllAlerts() (*[]Alert, error) {
	out := &[]Alert{}
	err := c.getObject("/alert", out)
	return out, err
}

func (c *Client) PostAlert(alert *Alert) (*Alert, error) {
	out := &Alert{}
	err := c.postObject(alert, "/alert", out)
	return out, err
}

func (c *Client) PutAlert(alert *Alert) (*Alert, error) {
	out := &Alert{}
	err := c.putObject(alert, "/alert", out)
	return out, err
}

func (c *Client) DeleteAlert(id piazza.Ident) error {
	err := c.deleteObject("/alert/" + id.String())
	return err
}

//------------------------------------------------------------------------------

func (c *Client) GetStats() (*workflowAdminStats, error) {
	out := &workflowAdminStats{}
	err := c.getObject("/admin/stats", out)
	return out, err

}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/url"

	"context"
)

type websocketResource struct {
	url.URL
}

func (*websocketResource) Await(context.Context) error {
	// TODO(uwe): Implement
	return fmt.Errorf("Not yet implemented. Pull-requests are welcome")
}
<commit_msg>Implement missing websocket resource<commit_after>package main

import (
	"net"
	"net/http"
	"net/url"
	"time"

	"context"

	"github.com/gorilla/websocket"
)

type websocketResource struct {
	url.URL
}

func (r *websocketResource) Await(ctx context.Context) error {
	netDialer := &net.Dialer{}
	dial := func(network, address string) (net.Conn, error) {
		return netDialer.DialContext(ctx, network, address)
	}
	var timeout time.Duration
	if deadline, ok := ctx.Deadline(); ok {
		timeout = deadline.Sub(time.Now())
	}
	wsDialer := &websocket.Dialer{
		NetDial:          dial,
		HandshakeTimeout: timeout,
		Proxy:            http.ProxyFromEnvironment,
	}

	// IDEA(uwe): Use fragment to specify origin
	// IDEA(uwe): Use fragment to specify subprotocol
	// IDEA(uwe): Use fragment to specify cookies

	conn, _, err := wsDialer.Dial(r.URL.String(), nil)
	if err != nil {
		return ErrUnavailable
	}
	defer conn.Close()

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"goasgiserver/asgi"
	"log"
	"net/http"

	"github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
}

type websocketMessage struct {
	// Type of the message. Can be websocket.TextMessage or websocket.BinaryMessage.
	Type int

	// Value to send through the websocket connection.
	Content []byte
}

// Processes the read and write actions of a websocket connection. You can read
// from the read channel to get messages and write them to the write channel
// to send them. The websocket connection is closed, if the write channels is
// closed.
func processWebsocket(conn *websocket.Conn, read, write chan websocketMessage) {
	go func() {
		for message := range write {
			// Write messages from the write channel to the websocket connection.
			err := conn.WriteMessage(message.Type, message.Content)
			if err != nil {
				log.Printf("Could not send message: %s", err)
				break
			}
		}
		// If an error happens or the write channel is closed: close the websocket
		// connection.
		conn.Close()
	}()

	for {
		// Read messages from the websocket connection and write them to the write
		// channel.
		t, m, err := conn.ReadMessage()
		if err != nil {
			// If the websocket connection was closed, then err will be != nil.
			log.Printf("Could not receive the websocket message: %s", err)
			break
		}
		// Send the message to the channel
		read <- websocketMessage{Type: t, Content: m}
	}
	// If an error happens, close the websocket connection.
	conn.Close()
}

// Read from a channel (expecting a websocket.send!*** channel) and sends
// any data to the receive channel. Closes the channel if anything goes wrong.
func processChannelLayerReceive(channel string, receive chan asgi.SendCloseAcceptMessage) {
	for {
		// Read from the channel layer.
		var am asgi.SendCloseAcceptMessage
		_, err := channelLayer.Receive([]string{channel}, true, &am)
		if err != nil {
			log.Printf("could not get the message: %s", err)
			break
		}

		// Send the received value to the channel.
		receive <- am
	}
	close(receive)
}

// Sends the websocket handshake to the channel layer. Returns the name of the
// reply channel.
func forwardWebsocketConnection(req *http.Request) (replyChannel string, err error) {
	// Create the name for the reply channel
	replyChannel, err = channelLayer.NewChannel("websocket.send!")
	if err != nil {
		return "", fmt.Errorf("could not create a new channel name: %s", err)
	}

	// Create the connection message object
	message := asgi.ConnectionMessage{
		ReplyChannel: replyChannel,
		Scheme:       req.URL.Scheme,
		Path:         req.URL.Path,
		QueryString:  []byte(req.URL.RawQuery),
		Headers:      req.Header,
		Client:       req.Host, //TODO use the right value
		Server:       req.Host,
	}

	// Send the message to the channel layer
	if err = channelLayer.Send("websocket.connect", &message); err != nil {
		if asgi.IsChannelFullError(err) {
			// Forward a ChannelFullError
			return "", err
		}
		return "", fmt.Errorf("can not send message %v: %s", message, err)
	}
	return replyChannel, nil
}

// Handles the response after a websocket connection. Returns the websocket connection
// if it was opend.
func receiveAccept(w http.ResponseWriter, req *http.Request, channel string) (conn *websocket.Conn, err error) {
	// Get a message from the channel layer.
	var am asgi.SendCloseAcceptMessage
	n, err := channelLayer.Receive([]string{channel}, true, &am)
	if err != nil {
		return nil, fmt.Errorf("could not get the message: %s", err)
	}

	// TODO: Test the case, that the http request was closed since we got the
	// the answer from the channellayer

	if n == "" {
		// Did not get any message from the channel layer.
		// The specs say, that the handshake should not be finished. But then we are
		// not compatible to reconnecting-websocket that is used by the channels-examples.
		// So for now, we open the websocket connection.
		am.Accept = true
	}

	if am.Text != "" || am.Bytes != nil || am.Accept {
		// Finish the websocket handshake by upgrading the http request.
		conn, err = upgrader.Upgrade(w, req, nil)
		if err != nil {
			return nil, fmt.Errorf("could not upgrade the http request: %s", err)
		}

		// Send the first data, if there is one.
		if am.Text != "" {
			conn.WriteMessage(websocket.TextMessage, []byte(am.Text))
		} else if am.Bytes != nil {
			conn.WriteMessage(websocket.BinaryMessage, am.Bytes)
		}

		// The connection should be closed again.
		if am.Close != 0 {
			// The connection was opened but should be closed again
			if conn.CloseHandler()(am.Close, ""); err != nil {
				return nil, fmt.Errorf("Could not close the websocket connection: %s", err)
			}
		}
		return conn, nil
	}

	// If we are here, then the websocket connection should not be opend
	if am.Close == 0 {
		// At this point, close has to be set.
		return nil, fmt.Errorf("Got an send/close/accept message with all fields set to nil")
	}
	w.WriteHeader(403)
	return nil, nil
}

// Handels an request that wants to be upgraded to a websocket connection.
// Returns an error if one happen.
func asgiWebsocketHandler(w http.ResponseWriter, req *http.Request) error {
	// Send the request to the channel layer and get the reply channel name.
	replyChannel, err := forwardWebsocketConnection(req)
	if err != nil {
		if asgi.IsChannelFullError(err) {
			w.WriteHeader(503)
			return nil
		}
		return fmt.Errorf("could not establish websocket connection: %s", err)
	}

	// Try to receive the answer from the channel layer and open the websocket
	// connection, if it tells us to do.
	conn, err := receiveAccept(w, req, replyChannel)
	if err != nil {
		return fmt.Errorf("could not establish websocket connection: %s", err)
	}
	if conn == nil {
		// The websocket connection was not opend but no error occured. In this
		// case the http response was already send.
		return nil
	}

	// Close the websocket connection in the end.
	defer conn.Close()

	// Create goroutines to read/write to the websocket connection and the channel
	// layer at the same time.
	read := make(chan websocketMessage)
	write := make(chan websocketMessage)
	receive := make(chan asgi.SendCloseAcceptMessage)
	order := 0
	go processWebsocket(conn, read, write)
	go processChannelLayerReceive(replyChannel, receive)

	for {
		select {
		case r := <-read:
			// Received a message from the client
			order++
			message := asgi.ReceiveMessage{
				ReplyChannel: replyChannel,
				Path:         req.URL.Path,
				Content:      r.Content,
				Type:         r.Type,
				Order:        order,
			}

			// Forward it to the channel layer
			err = channelLayer.Send("websocket.receive", &message)
			if err != nil {
				if asgi.IsChannelFullError(err) {
					conn.CloseHandler()(websocket.CloseTryAgainLater, "Channel layer full.")
				}
				log.Printf("Could not send a message to channel layer: %s", err)
				return nil
			}

		case r, ok := <-receive:
			// Received a message from the channel layer
			if !ok {
				// The channel was closed. An error happend. So close the connection
				return nil
			}

			// Send the message to the websocket connection
			if r.Text != "" {
				write <- websocketMessage{
					Type:    websocket.TextMessage,
					Content: []byte(r.Text),
				}
			} else {
				write <- websocketMessage{
					Type:    websocket.BinaryMessage,
					Content: r.Bytes,
				}
			}
		}
	}
}
<commit_msg>Only send a websocket message to the client, if there is one<commit_after>package main

import (
	"fmt"
	"goasgiserver/asgi"
	"log"
	"net/http"

	"github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
}

type websocketMessage struct {
	// Type of the message. Can be websocket.TextMessage or websocket.BinaryMessage.
	Type int

	// Value to send through the websocket connection.
	Content []byte
}

// Processes the read and write actions of a websocket connection. You can read
// from the read channel to get messages and write them to the write channel
// to send them. The websocket connection is closed, if the write channels is
// closed.
func processWebsocket(conn *websocket.Conn, read, write chan websocketMessage) {
	go func() {
		for message := range write {
			// Write messages from the write channel to the websocket connection.
			err := conn.WriteMessage(message.Type, message.Content)
			if err != nil {
				log.Printf("Could not send message: %s", err)
				break
			}
		}
		// If an error happens or the write channel is closed: close the websocket
		// connection.
		conn.Close()
	}()

	for {
		// Read messages from the websocket connection and write them to the write
		// channel.
		t, m, err := conn.ReadMessage()
		if err != nil {
			// If the websocket connection was closed, then err will be != nil.
			log.Printf("Could not receive the websocket message: %s", err)
			break
		}
		// Send the message to the channel
		read <- websocketMessage{Type: t, Content: m}
	}
	// If an error happens, close the websocket connection.
	conn.Close()
}

// Read from a channel (expecting a websocket.send!*** channel) and sends
// any data to the receive channel. Closes the channel if anything goes wrong.
func processChannelLayerReceive(channel string, receive chan asgi.SendCloseAcceptMessage) {
	for {
		// Read from the channel layer.
		var am asgi.SendCloseAcceptMessage
		c, err := channelLayer.Receive([]string{channel}, true, &am)
		if err != nil {
			log.Printf("could not get the message: %s", err)
			break
		}
		if c != "" {
			// If we read the message from the channel (no timeout) then:
			// Send the received value to the channel.
			receive <- am
		}
	}
	close(receive)
}

// Sends the websocket handshake to the channel layer. Returns the name of the
// reply channel.
func forwardWebsocketConnection(req *http.Request) (replyChannel string, err error) {
	// Create the name for the reply channel
	replyChannel, err = channelLayer.NewChannel("websocket.send!")
	if err != nil {
		return "", fmt.Errorf("could not create a new channel name: %s", err)
	}

	// Create the connection message object
	message := asgi.ConnectionMessage{
		ReplyChannel: replyChannel,
		Scheme:       req.URL.Scheme,
		Path:         req.URL.Path,
		QueryString:  []byte(req.URL.RawQuery),
		Headers:      req.Header,
		Client:       req.Host, //TODO use the right value
		Server:       req.Host,
	}

	// Send the message to the channel layer
	if err = channelLayer.Send("websocket.connect", &message); err != nil {
		if asgi.IsChannelFullError(err) {
			// Forward a ChannelFullError
			return "", err
		}
		return "", fmt.Errorf("can not send message %v: %s", message, err)
	}
	return replyChannel, nil
}

// Handles the response after a websocket connection. Returns the websocket connection
// if it was opend.
func receiveAccept(w http.ResponseWriter, req *http.Request, channel string) (conn *websocket.Conn, err error) {
	// Get a message from the channel layer.
	var am asgi.SendCloseAcceptMessage
	n, err := channelLayer.Receive([]string{channel}, true, &am)
	if err != nil {
		return nil, fmt.Errorf("could not get the message: %s", err)
	}

	// TODO: Test the case, that the http request was closed since we got the
	// the answer from the channellayer

	if n == "" {
		// Did not get any message from the channel layer.
		// The specs say, that the handshake should not be finished. But then we are
		// not compatible to reconnecting-websocket that is used by the channels-examples.
		// So for now, we open the websocket connection.
		am.Accept = true
	}

	if am.Text != "" || am.Bytes != nil || am.Accept {
		// Finish the websocket handshake by upgrading the http request.
		conn, err = upgrader.Upgrade(w, req, nil)
		if err != nil {
			return nil, fmt.Errorf("could not upgrade the http request: %s", err)
		}

		// Send the first data, if there is one.
		if am.Text != "" {
			conn.WriteMessage(websocket.TextMessage, []byte(am.Text))
		} else if am.Bytes != nil {
			conn.WriteMessage(websocket.BinaryMessage, am.Bytes)
		}

		// The connection should be closed again.
		if am.Close != 0 {
			// The connection was opened but should be closed again
			if conn.CloseHandler()(am.Close, ""); err != nil {
				return nil, fmt.Errorf("Could not close the websocket connection: %s", err)
			}
		}
		return conn, nil
	}

	// If we are here, then the websocket connection should not be opend
	if am.Close == 0 {
		// At this point, close has to be set.
		return nil, fmt.Errorf("Got an send/close/accept message with all fields set to nil")
	}
	w.WriteHeader(403)
	return nil, nil
}

// Handels an request that wants to be upgraded to a websocket connection.
// Returns an error if one happen.
func asgiWebsocketHandler(w http.ResponseWriter, req *http.Request) error {
	// Send the request to the channel layer and get the reply channel name.
	replyChannel, err := forwardWebsocketConnection(req)
	if err != nil {
		if asgi.IsChannelFullError(err) {
			w.WriteHeader(503)
			return nil
		}
		return fmt.Errorf("could not establish websocket connection: %s", err)
	}

	// Try to receive the answer from the channel layer and open the websocket
	// connection, if it tells us to do.
	conn, err := receiveAccept(w, req, replyChannel)
	if err != nil {
		return fmt.Errorf("could not establish websocket connection: %s", err)
	}
	if conn == nil {
		// The websocket connection was not opend but no error occured. In this
		// case the http response was already send.
		return nil
	}

	// Close the websocket connection in the end.
	defer conn.Close()

	// Create goroutines to read/write to the websocket connection and the channel
	// layer at the same time.
	read := make(chan websocketMessage)
	write := make(chan websocketMessage)
	receive := make(chan asgi.SendCloseAcceptMessage)
	order := 0
	go processWebsocket(conn, read, write)
	go processChannelLayerReceive(replyChannel, receive)

	for {
		select {
		case r := <-read:
			// Received a message from the client
			order++
			message := asgi.ReceiveMessage{
				ReplyChannel: replyChannel,
				Path:         req.URL.Path,
				Content:      r.Content,
				Type:         r.Type,
				Order:        order,
			}

			// Forward it to the channel layer
			err = channelLayer.Send("websocket.receive", &message)
			if err != nil {
				if asgi.IsChannelFullError(err) {
					conn.CloseHandler()(websocket.CloseTryAgainLater, "Channel layer full.")
				}
				log.Printf("Could not send a message to channel layer: %s", err)
				return nil
			}

		case r, ok := <-receive:
			// Received a message from the channel layer
			if !ok {
				// The channel was closed. An error happend. So close the connection
				return nil
			}

			// Send the message to the websocket connection
			if r.Text != "" {
				write <- websocketMessage{
					Type:    websocket.TextMessage,
					Content: []byte(r.Text),
				}
			} else {
				write <- websocketMessage{
					Type:    websocket.BinaryMessage,
					Content: r.Bytes,
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>package cfmysql

import (
	"code.cloudfoundry.org/cli/cf/errors"
	"fmt"
	"os"
	"os/exec"
	"strconv"
	"strings"
)

//go:generate counterfeiter . MysqlRunner
type MysqlRunner interface {
	RunMysql(hostname string, port int, dbName string, username string, password string, args ...string) error
	RunMysqlDump(hostname string, port int, dbName string, username string, password string, args ...string) error
}

func NewMysqlRunner(execWrapper ExecWrapper) MysqlRunner {
	return &mysqlRunner{
		execWrapper: execWrapper,
	}
}

type mysqlRunner struct {
	execWrapper ExecWrapper
}

func (self *mysqlRunner) RunMysql(hostname string, port int, dbName string, username string, password string, mysqlArgs ...string) error {
	path, err := self.execWrapper.LookPath("mysql")
	if err != nil {
		return errors.New("'mysql' client not found in PATH")
	}

	args := []string{"-u", username, "-p" + password, "-h", hostname, "-P", strconv.Itoa(port)}
	args = append(args, mysqlArgs...)
	args = append(args, dbName)

	cmd := exec.Command(path, args...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	err = self.execWrapper.Run(cmd)
	if err != nil {
		return fmt.Errorf("error running mysql client: %s", err)
	}

	return nil
}

func (self *mysqlRunner) RunMysqlDump(hostname string, port int, dbName string, username string, password string, mysqlDumpArgs ...string) error {
	path, err := self.execWrapper.LookPath("mysqldump")
	if err != nil {
		return errors.New("'mysqldump' not found in PATH")
	}

	var tableArgs []string
	nonTableArgs := mysqlDumpArgs[0:]

	for i, argument := range mysqlDumpArgs {
		if strings.HasPrefix(argument, "-") {
			break
		}

		tableArgs = append(tableArgs, argument)
		nonTableArgs = mysqlDumpArgs[i+1:]
	}

	args := []string{"-u", username, "-p" + password, "-h", hostname, "-P", strconv.Itoa(port)}
	args = append(args, nonTableArgs...)
	args = append(args, dbName)
	args = append(args, tableArgs...)

	cmd := exec.Command(path, args...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	err = self.execWrapper.Run(cmd)
	if err != nil {
		return fmt.Errorf("error running mysqldump: %s", err)
	}

	return nil
}

func (self *mysqlRunner) MakeMysqlCommand(hostname string, port int, dbName string, username string, password string) *exec.Cmd {
	return exec.Command("mysql", "-u", "username", "-p"+password, "-h", "hostname", "-P", strconv.Itoa(port), dbName)
}
<commit_msg>Remove unused code<commit_after>package cfmysql

import (
	"code.cloudfoundry.org/cli/cf/errors"
	"fmt"
	"os"
	"os/exec"
	"strconv"
	"strings"
)

//go:generate counterfeiter . MysqlRunner
type MysqlRunner interface {
	RunMysql(hostname string, port int, dbName string, username string, password string, args ...string) error
	RunMysqlDump(hostname string, port int, dbName string, username string, password string, args ...string) error
}

func NewMysqlRunner(execWrapper ExecWrapper) MysqlRunner {
	return &mysqlRunner{
		execWrapper: execWrapper,
	}
}

type mysqlRunner struct {
	execWrapper ExecWrapper
}

func (self *mysqlRunner) RunMysql(hostname string, port int, dbName string, username string, password string, mysqlArgs ...string) error {
	path, err := self.execWrapper.LookPath("mysql")
	if err != nil {
		return errors.New("'mysql' client not found in PATH")
	}

	args := []string{"-u", username, "-p" + password, "-h", hostname, "-P", strconv.Itoa(port)}
	args = append(args, mysqlArgs...)
	args = append(args, dbName)

	cmd := exec.Command(path, args...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	err = self.execWrapper.Run(cmd)
	if err != nil {
		return fmt.Errorf("error running mysql client: %s", err)
	}

	return nil
}

func (self *mysqlRunner) RunMysqlDump(hostname string, port int, dbName string, username string, password string, mysqlDumpArgs ...string) error {
	path, err := self.execWrapper.LookPath("mysqldump")
	if err != nil {
		return errors.New("'mysqldump' not found in PATH")
	}

	var tableArgs []string
	nonTableArgs := mysqlDumpArgs[0:]

	for i, argument := range mysqlDumpArgs {
		if strings.HasPrefix(argument, "-") {
			break
		}

		tableArgs = append(tableArgs, argument)
		nonTableArgs = mysqlDumpArgs[i+1:]
	}

	args := []string{"-u", username, "-p" + password, "-h", hostname, "-P", strconv.Itoa(port)}
	args = append(args, nonTableArgs...)
	args = append(args, dbName)
	args = append(args, tableArgs...)

	cmd := exec.Command(path, args...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	err = self.execWrapper.Run(cmd)
	if err != nil {
		return fmt.Errorf("error running mysqldump: %s", err)
	}

	return nil
}
<|endoftext|>


<commit_before>package slack

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"net"
	"net/url"
	"sync"
	"time"

	"golang.org/x/net/websocket"
)

const (
	EV_MESSAGE = iota
	EV_USER_TYPING
)

type SlackWS struct {
	conn      *websocket.Conn
	messageId int
	mutex     sync.Mutex
	Slack
}

var portMapping = map[string]string{"ws": "80", "wss": "443"}

func fixUrlPort(orig string) (string, error) {
	urlObj, err := url.ParseRequestURI(orig)
	if err != nil {
		return "", err
	}
	_, _, err = net.SplitHostPort(urlObj.Host)
	if err != nil {
		return urlObj.Scheme + "://" + urlObj.Host + ":" + portMapping[urlObj.Scheme] + urlObj.Path, nil
	}
	return orig, nil
}

func (api *Slack) StartRTM(protocol, origin string) (*SlackWS, error) {
	response := &infoResponseFull{}
	err := parseResponse("rtm.start", url.Values{"token": {api.config.token}}, response, api.debug)
	if err != nil {
		return nil, err
	}
	if !response.Ok {
		return nil, errors.New(response.Error)
	}
	api.info = response.Info
	// websocket.Dial does not accept url without the port (yet)
	// Fixed by: https://github.com/golang/net/commit/5058c78c3627b31e484a81463acd51c7cecc06f3
	// but slack returns the address with no port, so we have to fix it
	api.info.Url, err = fixUrlPort(api.info.Url)
	if err != nil {
		return nil, err
	}
	api.config.protocol, api.config.origin = protocol, origin
	wsApi := &SlackWS{Slack: *api}
	wsApi.conn, err = websocket.Dial(api.info.Url, api.config.protocol, api.config.origin)
	if err != nil {
		return nil, err
	}
	return wsApi, nil
}

func (api *SlackWS) Ping() error {
	api.mutex.Lock()
	defer api.mutex.Unlock()
	api.messageId++
	msg := &Ping{Id: api.messageId, Type: "ping"}
	if err := websocket.JSON.Send(api.conn, msg); err != nil {
		return err
	}
	return nil
}

func (api *SlackWS) Keepalive(interval time.Duration) {
	ticker := time.NewTicker(interval)

	for {
		select {
		case <-ticker.C:
			if err := api.Ping(); err != nil {
				log.Fatal(err)
			}
		}
	}
}

func (api *SlackWS) SendMessage(msg *OutgoingMessage) error {
	if msg == nil {
		return fmt.Errorf("Can't send a nil message")
	}

	if err := websocket.JSON.Send(api.conn, *msg); err != nil {
		return err
	}
	return nil
}

func (api *SlackWS) HandleIncomingEvents(ch *chan SlackEvent) {
	event := json.RawMessage{}
	for {
		if err := websocket.JSON.Receive(api.conn, &event); err == io.EOF {
			//log.Println("Derpi derp, should we destroy conn and start over?")
			//if err = api.StartRTM(); err != nil {
			//	log.Fatal(err)
			//}
			// should we reconnect here?
			if !api.conn.IsClientConn() {
				api.conn, err = websocket.Dial(api.info.Url, api.config.protocol, api.config.origin)
				if err != nil {
					log.Panic(err)
				}
			}
			// XXX: check for timeout and implement exponential backoff
		} else if err != nil {
			log.Panic(err)
		}
		if len(event) == 0 {
			log.Println("Event Empty. WTF?")
		} else {
			if api.debug {
				log.Println(string(event[:]))
			}
			handleEvent(ch, event)
		}
		time.Sleep(time.Millisecond * 500)
	}
}

func handleEvent(ch *chan SlackEvent, event json.RawMessage) {
	em := Event{}
	err := json.Unmarshal(event, &em)
	if err != nil {
		log.Fatal(err)
	}
	switch em.Type {
	case "":
		// try ok
		ack := AckMessage{}
		if err = json.Unmarshal(event, &ack); err != nil {
			log.Fatal(err)
		}
		if ack.Ok {
			log.Printf("Received an ok for: %d", ack.ReplyTo)
		} else {
			log.Println(event)
			log.Println("XXX: ?")
		}
	case "hello":
		return
	case "pong":
		// XXX: Eventually check to which ping this matched with
		//      Allows us to have stats about latency and what not
		return
	case "presence_change":
		//log.Printf("`%s is %s`\n", info.GetUserById(event.PUserId).Name, event.Presence)
	case "message":
		handleMessage(ch, event)
	case "channel_marked":
		log.Printf("XXX: To implement %s", em)
	case "user_typing":
		handleUserTyping(ch, event)
	default:
		log.Println("XXX: " + string(event))
	}
}

func handleUserTyping(ch *chan SlackEvent, event json.RawMessage) {
	msg := UserTyping{}
	if err := json.Unmarshal(event, &msg); err != nil {
		log.Fatal(err)
	}
	*ch <- SlackEvent{Type: EV_USER_TYPING, Data: msg}
}

func handleMessage(ch *chan SlackEvent, event json.RawMessage) {
	msg := Message{}
	err := json.Unmarshal(event, &msg)
	if err != nil {
		log.Fatal(err)
	}
	*ch <- SlackEvent{Type: EV_MESSAGE, Data: msg}
}
<commit_msg>It is good form to always stop a ticker<commit_after>package slack

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"net"
	"net/url"
	"sync"
	"time"

	"golang.org/x/net/websocket"
)

const (
	EV_MESSAGE = iota
	EV_USER_TYPING
)

type SlackWS struct {
	conn      *websocket.Conn
	messageId int
	mutex     sync.Mutex
	Slack
}

var portMapping = map[string]string{"ws": "80", "wss": "443"}

func fixUrlPort(orig string) (string, error) {
	urlObj, err := url.ParseRequestURI(orig)
	if err != nil {
		return "", err
	}
	_, _, err = net.SplitHostPort(urlObj.Host)
	if err != nil {
		return urlObj.Scheme + "://" + urlObj.Host + ":" + portMapping[urlObj.Scheme] + urlObj.Path, nil
	}
	return orig, nil
}

func (api *Slack) StartRTM(protocol, origin string) (*SlackWS, error) {
	response := &infoResponseFull{}
	err := parseResponse("rtm.start", url.Values{"token": {api.config.token}}, response, api.debug)
	if err != nil {
		return nil, err
	}
	if !response.Ok {
		return nil, errors.New(response.Error)
	}
	api.info = response.Info
	// websocket.Dial does not accept url without the port (yet)
	// Fixed by: https://github.com/golang/net/commit/5058c78c3627b31e484a81463acd51c7cecc06f3
	// but slack returns the address with no port, so we have to fix it
	api.info.Url, err = fixUrlPort(api.info.Url)
	if err != nil {
		return nil, err
	}
	api.config.protocol, api.config.origin = protocol, origin
	wsApi := &SlackWS{Slack: *api}
	wsApi.conn, err = websocket.Dial(api.info.Url, api.config.protocol, api.config.origin)
	if err != nil {
		return nil, err
	}
	return wsApi, nil
}

func (api *SlackWS) Ping() error {
	api.mutex.Lock()
	defer api.mutex.Unlock()
	api.messageId++
	msg := &Ping{Id: api.messageId, Type: "ping"}
	if err := websocket.JSON.Send(api.conn, msg); err != nil {
		return err
	}
	return nil
}

func (api *SlackWS) Keepalive(interval time.Duration) {
	ticker := time.NewTicker(interval)
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			if err := api.Ping(); err != nil {
				log.Fatal(err)
			}
		}
	}
}

func (api *SlackWS) SendMessage(msg *OutgoingMessage) error {
	if msg == nil {
		return fmt.Errorf("Can't send a nil message")
	}

	if err := websocket.JSON.Send(api.conn, *msg); err != nil {
		return err
	}
	return nil
}

func (api *SlackWS) HandleIncomingEvents(ch *chan SlackEvent) {
	event := json.RawMessage{}
	for {
		if err := websocket.JSON.Receive(api.conn, &event); err == io.EOF {
			//log.Println("Derpi derp, should we destroy conn and start over?")
			//if err = api.StartRTM(); err != nil {
			//	log.Fatal(err)
			//}
			// should we reconnect here?
			if !api.conn.IsClientConn() {
				api.conn, err = websocket.Dial(api.info.Url, api.config.protocol, api.config.origin)
				if err != nil {
					log.Panic(err)
				}
			}
			// XXX: check for timeout and implement exponential backoff
		} else if err != nil {
			log.Panic(err)
		}
		if len(event) == 0 {
			log.Println("Event Empty. WTF?")
		} else {
			if api.debug {
				log.Println(string(event[:]))
			}
			handleEvent(ch, event)
		}
		time.Sleep(time.Millisecond * 500)
	}
}

func handleEvent(ch *chan SlackEvent, event json.RawMessage) {
	em := Event{}
	err := json.Unmarshal(event, &em)
	if err != nil {
		log.Fatal(err)
	}
	switch em.Type {
	case "":
		// try ok
		ack := AckMessage{}
		if err = json.Unmarshal(event, &ack); err != nil {
			log.Fatal(err)
		}
		if ack.Ok {
			log.Printf("Received an ok for: %d", ack.ReplyTo)
		} else {
			log.Println(event)
			log.Println("XXX: ?")
		}
	case "hello":
		return
	case "pong":
		// XXX: Eventually check to which ping this matched with
		//      Allows us to have stats about latency and what not
		return
	case "presence_change":
		//log.Printf("`%s is %s`\n", info.GetUserById(event.PUserId).Name, event.Presence)
	case "message":
		handleMessage(ch, event)
	case "channel_marked":
		log.Printf("XXX: To implement %s", em)
	case "user_typing":
		handleUserTyping(ch, event)
	default:
		log.Println("XXX: " + string(event))
	}
}

func handleUserTyping(ch *chan SlackEvent, event json.RawMessage) {
	msg := UserTyping{}
	if err := json.Unmarshal(event, &msg); err != nil {
		log.Fatal(err)
	}
	*ch <- SlackEvent{Type: EV_USER_TYPING, Data: msg}
}

func handleMessage(ch *chan SlackEvent, event json.RawMessage) {
	msg := Message{}
	err := json.Unmarshal(event, &msg)
	if err != nil {
		log.Fatal(err)
	}
	*ch <- SlackEvent{Type: EV_MESSAGE, Data: msg}
}
<|endoftext|>


<commit_before>// Copyright 2012 Junqing Tan <ivan@mysqlab.net> and The Go Authors
// Use of this source code is governed by a BSD-style
// Part of source code is from Go fcgi package

package fcgiclient

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"sync"
	"errors"
	"io"
	"net"
  "strings"
  "strconv"
  "net/http"
)

const FCGI_LISTENSOCK_FILENO uint8 = 0
const FCGI_HEADER_LEN uint8 = 8
const VERSION_1 uint8 = 1
const FCGI_NULL_REQUEST_ID uint8 = 0
const FCGI_KEEP_CONN uint8 = 1
const doubleCRLF = "\r\n\r\n"

const (
	FCGI_BEGIN_REQUEST uint8 = iota + 1
	FCGI_ABORT_REQUEST
	FCGI_END_REQUEST
	FCGI_PARAMS
	FCGI_STDIN
	FCGI_STDOUT
	FCGI_STDERR
	FCGI_DATA
	FCGI_GET_VALUES
	FCGI_GET_VALUES_RESULT
	FCGI_UNKNOWN_TYPE
	FCGI_MAXTYPE = FCGI_UNKNOWN_TYPE
)

const (
	FCGI_RESPONDER uint8 = iota + 1
	FCGI_AUTHORIZER
	FCGI_FILTER
)

const (
	FCGI_REQUEST_COMPLETE uint8 = iota
	FCGI_CANT_MPX_CONN
	FCGI_OVERLOADED
	FCGI_UNKNOWN_ROLE
)

const (
	FCGI_MAX_CONNS  string = "MAX_CONNS"
	FCGI_MAX_REQS   string = "MAX_REQS"
	FCGI_MPXS_CONNS string = "MPXS_CONNS"
)

const (
	maxWrite = 65500 // 65530 may work, but for compatibility
	maxPad   = 255
)

type header struct {
	Version       uint8
	Type          uint8
	Id            uint16
	ContentLength uint16
	PaddingLength uint8
	Reserved      uint8
}

// for padding so we don't have to allocate all the time
// not synchronized because we don't care what the contents are
var pad [maxPad]byte

func (h *header) init(recType uint8, reqId uint16, contentLength int) {
	h.Version = 1
	h.Type = recType
	h.Id = reqId
	h.ContentLength = uint16(contentLength)
	h.PaddingLength = uint8(-contentLength & 7)
}

type record struct {
	h   header
	rbuf []byte
}

func (rec *record) read(r io.Reader) (buf []byte, err error) {
	if err = binary.Read(r, binary.BigEndian, &rec.h); err != nil {
		return
	}
	if rec.h.Version != 1 {
    err = errors.New("fcgi: invalid header version")
		return
	}
  if rec.h.Type == FCGI_END_REQUEST {
    err = io.EOF
    return
  }
	n := int(rec.h.ContentLength) + int(rec.h.PaddingLength)
	if len(rec.rbuf) < n {
	  rec.rbuf = make([]byte, n)
	}
	if n, err = io.ReadFull(r, rec.rbuf[:n]); err != nil {
		return
	}
	buf = rec.rbuf[:int(rec.h.ContentLength)]

	return
}

type FCGIClient struct {
	mutex     sync.Mutex
	rwc       io.ReadWriteCloser
	h         header
	buf 	  bytes.Buffer
	keepAlive bool
}

func New(t string, a string) (fcgi *FCGIClient, err error) {
	var conn net.Conn

	conn, err = net.Dial(t, a)
  if err != nil {
    return
  }

	fcgi = &FCGIClient{
		rwc:       conn,
		keepAlive: false,
	}

	return
}

func (this *FCGIClient) Close() {
  this.rwc.Close()
}

func (this *FCGIClient) writeRecord(recType uint8, reqId uint16, content []byte) ( err error) {
	this.mutex.Lock()
	defer this.mutex.Unlock()
	this.buf.Reset()
	this.h.init(recType, reqId, len(content))
	if err := binary.Write(&this.buf, binary.BigEndian, this.h); err != nil {
		return err
	}
	if _, err := this.buf.Write(content); err != nil {
		return err
	}
	if _, err := this.buf.Write(pad[:this.h.PaddingLength]); err != nil {
		return err
	}
	_, err = this.rwc.Write(this.buf.Bytes())
	return err
}

func (this *FCGIClient) writeBeginRequest(reqId uint16, role uint16, flags uint8) error {
	b := [8]byte{byte(role >> 8), byte(role), flags}
	return this.writeRecord(FCGI_BEGIN_REQUEST, reqId, b[:])
}

func (this *FCGIClient) writeEndRequest(reqId uint16, appStatus int, protocolStatus uint8) error {
	b := make([]byte, 8)
	binary.BigEndian.PutUint32(b, uint32(appStatus))
	b[4] = protocolStatus
	return this.writeRecord(FCGI_END_REQUEST, reqId, b)
}

func (this *FCGIClient) writePairs(recType uint8, reqId uint16, pairs map[string]string) error {
	w  := newWriter(this, recType, reqId)
	b  := make([]byte, 8)
	nn := 0
	for k, v := range pairs {
    m := 8 + len(k) + len(v)
    if m > maxWrite {
      // param data size exceed 65535 bytes"
      vl := maxWrite - 8 - len(k)
      v = v[:vl]
    }
    n := encodeSize(b, uint32(len(k)))
    n += encodeSize(b[n:], uint32(len(v)))
    m = n + len(k) + len(v)
    if (nn + m) > maxWrite {
      w.Flush()
      nn = 0
    }
    nn += m
    if _, err := w.Write(b[:n]); err != nil {
      return err
    }
    if _, err := w.WriteString(k); err != nil {
      return err
    }
    if _, err := w.WriteString(v); err != nil {
      return err
    }
  }
	w.Close()
	return nil
}

func readSize(s []byte) (uint32, int) {
	if len(s) == 0 {
		return 0, 0
	}
	size, n := uint32(s[0]), 1
	if size&(1<<7) != 0 {
		if len(s) < 4 {
			return 0, 0
		}
		n = 4
		size = binary.BigEndian.Uint32(s)
		size &^= 1 << 31
	}
	return size, n
}

func readString(s []byte, size uint32) string {
	if size > uint32(len(s)) {
		return ""
	}
	return string(s[:size])
}

func encodeSize(b []byte, size uint32) int {
	if size > 127 {
		size |= 1 << 31
		binary.BigEndian.PutUint32(b, size)
		return 4
	}
	b[0] = byte(size)
	return 1
}

// bufWriter encapsulates bufio.Writer but also closes the underlying stream when
// Closed.
type bufWriter struct {
	closer io.Closer
	*bufio.Writer
}

func (w *bufWriter) Close() error {
	if err := w.Writer.Flush(); err != nil {
		w.closer.Close()
		return err
	}
	return w.closer.Close()
}

func newWriter(c *FCGIClient, recType uint8, reqId uint16) *bufWriter {
	s := &streamWriter{c: c, recType: recType, reqId: reqId}
	w := bufio.NewWriterSize(s, maxWrite)
	return &bufWriter{s, w}
}

// streamWriter abstracts out the separation of a stream into discrete records.
// It only writes maxWrite bytes at a time.
type streamWriter struct {
	c       *FCGIClient
	recType uint8
	reqId   uint16
}

func (w *streamWriter) Write(p []byte) (int, error) {
	nn := 0
	for len(p) > 0 {
		n := len(p)
		if n > maxWrite {
			n = maxWrite
		}
		if err := w.c.writeRecord(w.recType, w.reqId, p[:n]); err != nil {
			return nn, err
		}
		nn += n
		p = p[n:]
	}
	return nn, nil
}

func (w *streamWriter) Close() error {
	// send empty record to close the stream
	return w.c.writeRecord(w.recType, w.reqId, nil)
}

// post data example: "key1=val1&key2=val2"
func (this *FCGIClient) Request(resp http.ResponseWriter, env map[string]string, post []byte) (ret []byte, err error) {

	var reqId uint16 = 1

  // set correct stdin length (required for php)
  env["CONTENT_LENGTH"] = strconv.Itoa(len(post))

	err = this.writeBeginRequest(reqId, uint16(FCGI_RESPONDER), 0)
	if err != nil {
		return
	}

	err = this.writePairs(FCGI_PARAMS, reqId, env)
	if err != nil {
		return
	}

	err = this.writeRecord(FCGI_STDIN, reqId, post)
	if err != nil {
		return
	}

  afterheader := false
	rec := &record{}
	for {
    buf, err := rec.read(this.rwc)
  	if err != nil {
  		break
  	}

    if afterheader {
      if resp != nil {
        resp.Write(buf)
      }
      ret = append(ret, buf...)
    } else {
      z := strings.SplitN(string(buf), doubleCRLF, 2)
      switch (len(z)) {
        case 2:
          if resp != nil {
            lines := strings.Split(z[0], "\n")
            for line := range lines  {
              v := strings.SplitN(lines[line], ":",2)
              if len(v) == 2 {
                resp.Header().Set(strings.TrimSpace(v[0]), strings.TrimSpace(v[1]))
              }
            }
            resp.Write([]byte(z[1]))
          }
          ret = append(ret, []byte(z[1])...)
        default:
          if resp != nil {
            resp.Write(buf)
          }
          ret = append(ret, buf...)
      }

      afterheader = true
    }
  }

	return
}
<commit_msg>add pasv mode: not return content from fcgi responder<commit_after>// Copyright 2012 Junqing Tan <ivan@mysqlab.net> and The Go Authors
// Use of this source code is governed by a BSD-style
// Part of source code is from Go fcgi package

package fcgiclient

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"sync"
	"errors"
	"io"
	"net"
  "strings"
  "strconv"
  "net/http"
)

const FCGI_LISTENSOCK_FILENO uint8 = 0
const FCGI_HEADER_LEN uint8 = 8
const VERSION_1 uint8 = 1
const FCGI_NULL_REQUEST_ID uint8 = 0
const FCGI_KEEP_CONN uint8 = 1
const doubleCRLF = "\r\n\r\n"

const (
	FCGI_BEGIN_REQUEST uint8 = iota + 1
	FCGI_ABORT_REQUEST
	FCGI_END_REQUEST
	FCGI_PARAMS
	FCGI_STDIN
	FCGI_STDOUT
	FCGI_STDERR
	FCGI_DATA
	FCGI_GET_VALUES
	FCGI_GET_VALUES_RESULT
	FCGI_UNKNOWN_TYPE
	FCGI_MAXTYPE = FCGI_UNKNOWN_TYPE
)

const (
	FCGI_RESPONDER uint8 = iota + 1
	FCGI_AUTHORIZER
	FCGI_FILTER
)

const (
	FCGI_REQUEST_COMPLETE uint8 = iota
	FCGI_CANT_MPX_CONN
	FCGI_OVERLOADED
	FCGI_UNKNOWN_ROLE
)

const (
	FCGI_MAX_CONNS  string = "MAX_CONNS"
	FCGI_MAX_REQS   string = "MAX_REQS"
	FCGI_MPXS_CONNS string = "MPXS_CONNS"
)

const (
	maxWrite = 65500 // 65530 may work, but for compatibility
	maxPad   = 255
)

type header struct {
	Version       uint8
	Type          uint8
	Id            uint16
	ContentLength uint16
	PaddingLength uint8
	Reserved      uint8
}

// for padding so we don't have to allocate all the time
// not synchronized because we don't care what the contents are
var pad [maxPad]byte

func (h *header) init(recType uint8, reqId uint16, contentLength int) {
	h.Version = 1
	h.Type = recType
	h.Id = reqId
	h.ContentLength = uint16(contentLength)
	h.PaddingLength = uint8(-contentLength & 7)
}

type record struct {
	h   header
	rbuf []byte
}

func (rec *record) read(r io.Reader) (buf []byte, err error) {
	if err = binary.Read(r, binary.BigEndian, &rec.h); err != nil {
		return
	}
	if rec.h.Version != 1 {
    err = errors.New("fcgi: invalid header version")
		return
	}
  if rec.h.Type == FCGI_END_REQUEST {
    err = io.EOF
    return
  }
	n := int(rec.h.ContentLength) + int(rec.h.PaddingLength)
	if len(rec.rbuf) < n {
	  rec.rbuf = make([]byte, n)
	}
	if n, err = io.ReadFull(r, rec.rbuf[:n]); err != nil {
		return
	}
	buf = rec.rbuf[:int(rec.h.ContentLength)]

	return
}

type FCGIClient struct {
	mutex     sync.Mutex
	rwc       io.ReadWriteCloser
	h         header
	buf 	  bytes.Buffer
	keepAlive bool
}

func New(t string, a string) (fcgi *FCGIClient, err error) {
	var conn net.Conn

	conn, err = net.Dial(t, a)
  if err != nil {
    return
  }

	fcgi = &FCGIClient{
		rwc:       conn,
		keepAlive: false,
	}

	return
}

func (this *FCGIClient) Close() {
  this.rwc.Close()
}

func (this *FCGIClient) writeRecord(recType uint8, reqId uint16, content []byte) ( err error) {
	this.mutex.Lock()
	defer this.mutex.Unlock()
	this.buf.Reset()
	this.h.init(recType, reqId, len(content))
	if err := binary.Write(&this.buf, binary.BigEndian, this.h); err != nil {
		return err
	}
	if _, err := this.buf.Write(content); err != nil {
		return err
	}
	if _, err := this.buf.Write(pad[:this.h.PaddingLength]); err != nil {
		return err
	}
	_, err = this.rwc.Write(this.buf.Bytes())
	return err
}

func (this *FCGIClient) writeBeginRequest(reqId uint16, role uint16, flags uint8) error {
	b := [8]byte{byte(role >> 8), byte(role), flags}
	return this.writeRecord(FCGI_BEGIN_REQUEST, reqId, b[:])
}

func (this *FCGIClient) writeEndRequest(reqId uint16, appStatus int, protocolStatus uint8) error {
	b := make([]byte, 8)
	binary.BigEndian.PutUint32(b, uint32(appStatus))
	b[4] = protocolStatus
	return this.writeRecord(FCGI_END_REQUEST, reqId, b)
}

func (this *FCGIClient) writePairs(recType uint8, reqId uint16, pairs map[string]string) error {
	w  := newWriter(this, recType, reqId)
	b  := make([]byte, 8)
	nn := 0
	for k, v := range pairs {
    m := 8 + len(k) + len(v)
    if m > maxWrite {
      // param data size exceed 65535 bytes"
      vl := maxWrite - 8 - len(k)
      v = v[:vl]
    }
    n := encodeSize(b, uint32(len(k)))
    n += encodeSize(b[n:], uint32(len(v)))
    m = n + len(k) + len(v)
    if (nn + m) > maxWrite {
      w.Flush()
      nn = 0
    }
    nn += m
    if _, err := w.Write(b[:n]); err != nil {
      return err
    }
    if _, err := w.WriteString(k); err != nil {
      return err
    }
    if _, err := w.WriteString(v); err != nil {
      return err
    }
  }
	w.Close()
	return nil
}

func readSize(s []byte) (uint32, int) {
	if len(s) == 0 {
		return 0, 0
	}
	size, n := uint32(s[0]), 1
	if size&(1<<7) != 0 {
		if len(s) < 4 {
			return 0, 0
		}
		n = 4
		size = binary.BigEndian.Uint32(s)
		size &^= 1 << 31
	}
	return size, n
}

func readString(s []byte, size uint32) string {
	if size > uint32(len(s)) {
		return ""
	}
	return string(s[:size])
}

func encodeSize(b []byte, size uint32) int {
	if size > 127 {
		size |= 1 << 31
		binary.BigEndian.PutUint32(b, size)
		return 4
	}
	b[0] = byte(size)
	return 1
}

// bufWriter encapsulates bufio.Writer but also closes the underlying stream when
// Closed.
type bufWriter struct {
	closer io.Closer
	*bufio.Writer
}

func (w *bufWriter) Close() error {
	if err := w.Writer.Flush(); err != nil {
		w.closer.Close()
		return err
	}
	return w.closer.Close()
}

func newWriter(c *FCGIClient, recType uint8, reqId uint16) *bufWriter {
	s := &streamWriter{c: c, recType: recType, reqId: reqId}
	w := bufio.NewWriterSize(s, maxWrite)
	return &bufWriter{s, w}
}

// streamWriter abstracts out the separation of a stream into discrete records.
// It only writes maxWrite bytes at a time.
type streamWriter struct {
	c       *FCGIClient
	recType uint8
	reqId   uint16
}

func (w *streamWriter) Write(p []byte) (int, error) {
	nn := 0
	for len(p) > 0 {
		n := len(p)
		if n > maxWrite {
			n = maxWrite
		}
		if err := w.c.writeRecord(w.recType, w.reqId, p[:n]); err != nil {
			return nn, err
		}
		nn += n
		p = p[n:]
	}
	return nn, nil
}

func (w *streamWriter) Close() error {
	// send empty record to close the stream
	return w.c.writeRecord(w.recType, w.reqId, nil)
}

// data(post) example: "key1=val1&key2=val2"
// do not return content when pasv is ture, only pass it to writer
func (this *FCGIClient) Request(resp http.ResponseWriter, env map[string]string, data []byte, pasv bool) (ret []byte, err error) {

	var reqId uint16 = 1

	// set correct stdin length (required for php)
	env["CONTENT_LENGTH"] = strconv.Itoa(len(data))
	if len(data) > 0 {
	  env["REQUEST_METHOD"] = "POST"
	}

	err = this.writeBeginRequest(reqId, uint16(FCGI_RESPONDER), 0)
	if err != nil {
		return
	}

	err = this.writePairs(FCGI_PARAMS, reqId, env)
	if err != nil {
		return
	}

	err = this.writeRecord(FCGI_STDIN, reqId, data)
	if err != nil {
		return
	}

  afterheader := false
	rec := &record{}
	for {
    buf, err := rec.read(this.rwc)
  	if err != nil {
  		break
  	}

    if afterheader {
      if resp != nil {
        resp.Write(buf)
      }
      if !pasv {
        ret = append(ret, buf...)
      }
    } else {
      ret = append(ret, buf...)
      z := strings.SplitN(string(ret), doubleCRLF, 2)
      switch (len(z)) {
        case 2:
          if resp != nil {
            lines := strings.Split(z[0], "\n")
            for line := range lines  {
              v := strings.SplitN(lines[line], ":",2)
              if len(v) == 2 {
                resp.Header().Set(strings.TrimSpace(v[0]), strings.TrimSpace(v[1]))
              }
            }
            resp.Write([]byte(z[1]))
          }
          if pasv {
            ret = ret[:0]
          }
        default:
          // wait until doubleCRLF
          continue
      }

      afterheader = true
    }
  }

	return
}
<|endoftext|>