Example #1
0
// RawPostHandler relays POST data directly to Dynamo, typically called by other endpoint proxy packages
// that are recognized by the string in the request path.
func RawPostHandler(w http.ResponseWriter, req *http.Request) {
	if bbpd_runinfo.BBPDAbortIfClosed(w) {
		return
	}
	if req.Method != "POST" {
		e := fmt.Sprintf("raw_post_route.RawPostHandler: method only supports POST")
		log.Printf(e)
		http.Error(w, e, http.StatusBadRequest)
		return
	}
	pathElts := strings.Split(req.URL.Path, "/")
	if len(pathElts) != 3 {
		e := "raw_post_route.RawPostHandler:cannot parse path"
		log.Printf(e)
		http.Error(w, e, http.StatusBadRequest)
		return
	}

	ue_ep, ue_err := url.QueryUnescape(string(pathElts[2]))
	if ue_err != nil {
		e := fmt.Sprintf("raw_table_route.RawPostHandler:cannot unescape %s, %s", string(pathElts[2]), ue_err.Error())
		log.Printf(e)
		http.Error(w, e, http.StatusInternalServerError)
		return
	}

	RawPostReq(w, req, ue_ep)
}
// DescribeTableHandler can be used via POST (passing in JSON) or GET (as /DescribeTable/TableName).
func DescribeTableHandler(w http.ResponseWriter, req *http.Request) {
	if bbpd_runinfo.BBPDAbortIfClosed(w) {
		return
	}
	if req.Method == "POST" {
		describeTable_POST_Handler(w, req)
	} else {
		e := fmt.Sprintf("describe_tables_route.DescribeTablesHandler:bad method %s", req.Method)
		log.Printf(e)
		http.Error(w, e, http.StatusInternalServerError)
	}
}
Example #3
0
// RawPostReq obtains the POST payload from the request and forwards it on to the endpoint amzTarget.
func RawPostReq(w http.ResponseWriter, req *http.Request, amzTarget string) {
	if bbpd_runinfo.BBPDAbortIfClosed(w) {
		return
	}
	start := time.Now()
	bodybytes, read_err := ioutil.ReadAll(req.Body)
	req.Body.Close()
	if read_err != nil && read_err != io.EOF {
		e := fmt.Sprintf("raw_post_route.RawPostReq err reading req body: %s", read_err.Error())
		log.Printf(e)
		http.Error(w, e, http.StatusInternalServerError)
		return
	}

	resp_body, code, resp_err := authreq.RetryReqJSON_V4(bodybytes, amzTarget)

	if resp_err != nil {
		e := fmt.Sprintf("raw_post_route.RawPostReq: resp err calling %s err %s (input json: %s)",
			amzTarget, resp_err.Error(), string(bodybytes))
		log.Printf(e)
		http.Error(w, e, http.StatusInternalServerError)
		return
	}

	if ep.HttpErr(code) {
		e := fmt.Sprintf("raw_post_route.RawPostReq: http err %d calling %s (input json: %s)",
			code, amzTarget, string(bodybytes))
		route_response.WriteError(w, code, e, resp_body)
		return
	}

	mr_err := route_response.MakeRouteResponse(
		w,
		req,
		resp_body,
		code,
		start,
		amzTarget)
	if mr_err != nil {
		e := fmt.Sprintf("raw_post_route.RawPostReq %s", mr_err.Error())
		log.Printf(e)
	}
}
Example #4
0
// StatusHandler displays available handlers.
func statusHandler(w http.ResponseWriter, req *http.Request) {
	if bbpd_runinfo.BBPDAbortIfClosed(w) {
		return
	}
	if req.Method != "GET" {
		e := "method only supports GET"
		http.Error(w, e, http.StatusBadRequest)
		return
	}
	var ss Status_Struct
	ss.Args = make(map[string]string)
	ss.Status = "ready"

	ss.Args[bbpd_const.X_BBPD_VERBOSE] = "set '-H \"X-Bbpd-Verbose: True\" ' to get verbose output"
	ss.Args[bbpd_const.X_BBPD_INDENT] = "set '-H \"X-Bbpd-Indent: True\" ' to indent the top-level json"
	ss.AvailableHandlers = availableHandlers
	ss.Summary = bbpd_stats.GetSummary()
	sj, sj_err := json.Marshal(ss)
	if sj_err != nil {
		e := fmt.Sprintf("bbpd_route.statusHandler:status marshal err %s", sj_err.Error())
		log.Printf(e)
		http.Error(w, e, http.StatusInternalServerError)
		return
	}

	mr_err := route_response.MakeRouteResponse(
		w,
		req,
		sj,
		http.StatusOK,
		time.Now(),
		"Status")
	if mr_err != nil {
		e := fmt.Sprintf("bbpd_route.StatusHandler %s", mr_err.Error())
		log.Printf(e)
	}
}
Example #5
0
// UpdateItemHandler relays the UpdateItem request to Dynamo but first validates it through a local type.
func UpdateItemHandler(w http.ResponseWriter, req *http.Request) {
	if bbpd_runinfo.BBPDAbortIfClosed(w) {
		return
	}
	start := time.Now()
	if req.Method != "POST" {
		e := "update_item_route.UpdateItemHandler:method only supports POST"
		log.Printf(e)
		http.Error(w, e, http.StatusBadRequest)
		return
	}
	pathElts := strings.Split(req.URL.Path, "/")
	if len(pathElts) != 2 {
		e := "update_item_route.UpdateItemHandler:cannot parse path. try /update-item"
		log.Printf(e)
		http.Error(w, e, http.StatusBadRequest)
		return
	}

	bodybytes, read_err := ioutil.ReadAll(req.Body)
	req.Body.Close()
	if read_err != nil && read_err != io.EOF {
		e := fmt.Sprintf("update_item_route.UpdateItemHandler err reading req body: %s", read_err.Error())
		log.Printf(e)
		http.Error(w, e, http.StatusInternalServerError)
		return
	}

	u := update_item.NewUpdateItem()
	um_err := json.Unmarshal(bodybytes, u)

	if um_err != nil {
		e := fmt.Sprintf("update_item_route.UpdateItemHandler unmarshal err on %s to Update: %s", string(bodybytes), um_err.Error())
		log.Printf(e)
		http.Error(w, e, http.StatusInternalServerError)
		return
	}

	resp_body, code, resp_err := u.EndpointReq()

	if resp_err != nil {
		e := fmt.Sprintf("update_item_route.UpdateItemHandler:err %s",
			resp_err.Error())
		log.Printf(e)
		http.Error(w, e, http.StatusInternalServerError)
		return
	}

	if ep.HttpErr(code) {
		route_response.WriteError(w, code, "update_item_route.UpdateItemHandler", resp_body)
		return
	}

	mr_err := route_response.MakeRouteResponse(
		w,
		req,
		resp_body,
		code,
		start,
		update_item.ENDPOINT_NAME)
	if mr_err != nil {
		e := fmt.Sprintf("update_item_route.UpdateItemHandler %s", mr_err.Error())
		log.Printf(e)
	}
}
// StatusTableHandler is not a standard endpoint. It can be used to poll a table for readiness
// after a CreateTable or UpdateTable request.
func StatusTableHandler(w http.ResponseWriter, req *http.Request) {
	if bbpd_runinfo.BBPDAbortIfClosed(w) {
		return
	}
	start := time.Now()
	if req.Method != "GET" {
		e := "describe_table_route.StatusTableHandler:method only supports GET"
		log.Printf(e)
		http.Error(w, e, http.StatusBadRequest)
		return
	}
	pathElts := strings.Split(req.URL.Path, "/")
	if len(pathElts) != 3 {
		e := "describe_table_route.StatusTableHandler:cannot parse path. try /status-table/TABLENAME"
		log.Printf(e)
		http.Error(w, e, http.StatusBadRequest)
		return
	}
	ue_tn, ue_err := url.QueryUnescape(string(pathElts[2]))
	if ue_err != nil {
		e := fmt.Sprintf("cannot unescape %s, %s",
			string(pathElts[2]), ue_err.Error())
		log.Printf(e)
		http.Error(w, e, http.StatusInternalServerError)
		return
	}

	status := "ACTIVE" // our default key
	if query_status, status_ok := req.URL.Query()["status"]; status_ok {
		status = query_status[0]
	}

	poll := false
	if query_poll, poll_ok := req.URL.Query()["poll"]; poll_ok {
		if query_poll[0] == "1" || query_poll[0] == "yes" {
			poll = true
		}
	}

	tries := 1
	if poll {
		tries = 50
	}
	is_status, status_err := desc.PollTableStatus(
		ue_tn,
		status,
		tries)

	if status_err != nil {
		e := fmt.Sprintf("describe_table_route.StatusTableHandler:cannot get status %s from %s, err %s", status, ue_tn, status_err.Error())
		log.Printf(e)
		http.Error(w, e, http.StatusInternalServerError)
		return
	}

	s := desc.StatusResult{StatusResult: is_status}
	sj, sjerr := json.Marshal(s)
	if sjerr != nil {
		e := fmt.Sprintf("describe_table_route.StatusTableHandler:cannot get convert status to json, err %s", sjerr.Error())
		log.Printf(e)
		http.Error(w, e, http.StatusInternalServerError)
		return
	}
	end := time.Now()
	duration := fmt.Sprintf("%v", end.Sub(start))
	w.Header().Set(bbpd_const.CONTENTTYPE, bbpd_const.JSONMIME)
	b, json_err := json.Marshal(bbpd_msg.Response{
		Name:       desc.ENDPOINT_NAME,
		StatusCode: http.StatusOK,
		Body:       sj,
		Run: bbpd_msg.RunInfo{Method: req.Method,
			Host:     bbpd_const.LOCALHOST,
			Duration: duration,
			Start:    start,
			End:      end}})
	if json_err != nil {
		e := fmt.Sprintf("describe_table_route.StatusTableHandler:desc marshal failure %s", json_err.Error())
		log.Printf(e)
		http.Error(w, e, http.StatusInternalServerError)
		return
	}
	io.WriteString(w, string(b))
}
Example #7
0
// CreateTableHandler relays the CreateTable request to Dynamo but first validates it through a local type.
func CreateTableHandler(w http.ResponseWriter, req *http.Request) {
	if bbpd_runinfo.BBPDAbortIfClosed(w) {
		return
	}
	start := time.Now()
	if req.Method != "POST" {
		e := "create_table_route.CreateTableHandler:method only supports POST"
		log.Printf(e)
		http.Error(w, e, http.StatusBadRequest)
		return
	}
	pathElts := strings.Split(req.URL.Path, "/")
	if len(pathElts) != 2 {
		e := "create_table_route.CreateTableHandler:cannot parse path. try /create, call as POST"
		log.Printf(e)
		http.Error(w, e, http.StatusBadRequest)
		return
	}

	bodybytes, read_err := ioutil.ReadAll(req.Body)
	req.Body.Close()
	if read_err != nil && read_err != io.EOF {
		e := fmt.Sprintf("create_table_route.CreateTableHandler err reading req body: %s", read_err.Error())
		log.Printf(e)
		http.Error(w, e, http.StatusInternalServerError)
		return
	}

	c := create.NewCreate()
	um_err := json.Unmarshal(bodybytes, c)

	if um_err != nil {
		e := fmt.Sprintf("create_table_route.CreateTableHandler unmarshal err on %s to Create: %s", string(bodybytes), um_err.Error())
		log.Printf(e)
		http.Error(w, e, http.StatusInternalServerError)
		return
	}

	// the table name can't be too long, 256 bytes binary utf8
	if !create.ValidTableName(c.TableName) {
		e := fmt.Sprintf("create_table_route.CreateTableHandler: tablename over 256 bytes")
		log.Printf(e)
		http.Error(w, e, http.StatusBadRequest)
		return
	}

	resp_body, code, resp_err := c.EndpointReq()

	if resp_err != nil {
		e := fmt.Sprintf("create_table_route.CreateTableHandler:err %s",
			resp_err.Error())
		log.Printf(e)
		http.Error(w, e, http.StatusInternalServerError)
		return
	}

	if ep.HttpErr(code) {
		route_response.WriteError(w, code, "create_table_route.CreateTableHandler", resp_body)
		return
	}

	mr_err := route_response.MakeRouteResponse(
		w,
		req,
		resp_body,
		code,
		start,
		create.ENDPOINT_NAME)
	if mr_err != nil {
		e := fmt.Sprintf("create_table_route.CreateTableHandler %s", mr_err.Error())
		log.Printf(e)
	}
}
func BatchGetItemJSONHandler(w http.ResponseWriter, req *http.Request) {
	if bbpd_runinfo.BBPDAbortIfClosed(w) {
		return
	}
	start := time.Now()
	if req.Method != "POST" {
		e := "batch_get_item_route.BatchGetItemJSONHandler:method only supports POST"
		log.Printf(e)
		http.Error(w, e, http.StatusBadRequest)
		return
	}
	pathElts := strings.Split(req.URL.Path, "/")
	if len(pathElts) != 2 {
		e := "batch_get_item_route.BatchGetItemJSONHandler:cannot parse path. try /batch-get-item"
		log.Printf(e)
		http.Error(w, e, http.StatusBadRequest)
		return
	}

	bodybytes, read_err := ioutil.ReadAll(req.Body)
	if read_err != nil && read_err != io.EOF {
		e := fmt.Sprintf("batch_get_item_route.BatchGetItemJSONHandler err reading req body: %s", read_err.Error())
		log.Printf(e)
		http.Error(w, e, http.StatusInternalServerError)
		return
	}
	req.Body.Close()

	var b bgi.BatchGetItem

	um_err := json.Unmarshal(bodybytes, &b)
	if um_err != nil {
		e := fmt.Sprintf("batch_get_item_route.BatchGetItemJSONHandler unmarshal err on %s to BatchGetItem %s", string(bodybytes), um_err.Error())
		log.Printf(e)
		http.Error(w, e, http.StatusInternalServerError)
		return
	}

	if len(bodybytes) > bgi.QUERY_LIM_BYTES {
		e := fmt.Sprintf("batch_get_item_route.BatchGetItemJSONHandler - payload over 1024kb, may be rejected by aws! splitting into segmented requests will likely mean each segment is accepted")
		log.Printf(e)
	}

	resp_body, code, resp_err := b.DoBatchGet()

	if resp_err != nil {
		e := fmt.Sprintf("batch_get_item_route.BatchGetItemJSONHandler:err %s",
			resp_err.Error())
		log.Printf(e)
		http.Error(w, e, http.StatusInternalServerError)
		return
	}

	if ep.HttpErr(code) {
		route_response.WriteError(w, code, "batch_get_item_route.BatchGetItemJSONHandler", resp_body)
		return
	}

	// translate the Response to a ResponseItemsJSON
	resp := bgi.NewResponse()
	um_err = json.Unmarshal([]byte(resp_body), resp)
	if um_err != nil {
		e := fmt.Sprintf("batch_get_item_route.BatchGetItemJSONHandler:err %s",
			um_err.Error())
		log.Printf(e)
		http.Error(w, e, http.StatusInternalServerError)
		return
	}
	resp_json, rerr := resp.ToResponseItemsJSON()
	if rerr != nil {
		e := fmt.Sprintf("batch_get_item_route.BatchGetItemJSONHandler:err %s",
			rerr.Error())
		log.Printf(e)
		http.Error(w, e, http.StatusInternalServerError)
		return
	}
	json_body, jerr := json.Marshal(resp_json)
	if jerr != nil {
		e := fmt.Sprintf("batch_get_item_route.BatchGetItemJSONHandler:err %s",
			jerr.Error())
		log.Printf(e)
		http.Error(w, e, http.StatusInternalServerError)
		return
	}

	mr_err := route_response.MakeRouteResponse(
		w,
		req,
		json_body,
		http.StatusOK,
		start,
		bgi.ENDPOINT_NAME)
	if mr_err != nil {
		e := fmt.Sprintf("batch_get_item_route.BatchGetItemJSONHandler %s", mr_err.Error())
		log.Printf(e)
	}
}
Example #9
0
// BBPD-only endpoint.
// PutItemJSONHandler relays the PutItem request to Dynamo but first validates it through a local type.
// This variant allows the Item to be encoded as basic JSON. As there is always a conversion that
// needs to be performed from a PutIemJSON struct to a PutItem, this endpoint cannot utilize
// RawPost.
func PutItemJSONHandler(w http.ResponseWriter, req *http.Request) {
	if bbpd_runinfo.BBPDAbortIfClosed(w) {
		return
	}
	start := time.Now()
	if req.Method != "POST" {
		e := "put_item_route.PutItemJSONHandler:method only supports POST"
		log.Printf(e)
		http.Error(w, e, http.StatusBadRequest)
		return
	}
	pathElts := strings.Split(req.URL.Path, "/")
	if len(pathElts) != 2 {
		e := "put_item_route.PutItemJSONHandler:cannot parse path. try /put-item, call as POST"
		log.Printf(e)
		http.Error(w, e, http.StatusBadRequest)
		return
	}

	bodybytes, read_err := ioutil.ReadAll(req.Body)
	req.Body.Close()
	if read_err != nil && read_err != io.EOF {
		e := fmt.Sprintf("put_item_route.PutItemJSONHandler err reading req body: %s", read_err.Error())
		log.Printf(e)
		http.Error(w, e, http.StatusInternalServerError)
		return
	}

	p_json := put.NewPutItemJSON()
	um_err := json.Unmarshal(bodybytes, p_json)

	if um_err != nil {
		e := fmt.Sprintf("put_item_route.PutItemJSONHandler unmarshal err on %s to PutExpected: %s", string(bodybytes), um_err.Error())
		log.Printf(e)
		http.Error(w, e, http.StatusInternalServerError)
		return
	}

	p, perr := p_json.ToPutItem()
	if perr != nil {
		e := fmt.Sprintf("put_item_route.PutItemJSONHandler cannot convert PutItemJSON to PutItem:%s", perr.Error())
		log.Printf(e)
		http.Error(w, e, http.StatusInternalServerError)
		return
	}

	resp_body, code, resp_err := p.EndpointReq()

	if resp_err != nil {
		e := fmt.Sprintf("put_item_route.PutItemJSONHandler:err %s",
			resp_err.Error())
		log.Printf(e)
		http.Error(w, e, http.StatusInternalServerError)
		return
	}

	if ep.HttpErr(code) {
		route_response.WriteError(w, code, "put_item_route.PutItemJSONHandler", resp_body)
		return
	}

	mr_err := route_response.MakeRouteResponse(
		w,
		req,
		resp_body,
		code,
		start,
		put.ENDPOINT_NAME)
	if mr_err != nil {
		e := fmt.Sprintf("put_item_route.PutItemJSONHandler %s", mr_err.Error())
		log.Printf(e)
	}
}
Example #10
0
// CompatHandler allows bbpd to act as a partial pass-through proxy. Users can provide
// their own body and endpoint target header, but other headers are ignored.
// To use this, set headers with your http client. For example, with curl:
// curl -H "X-Amz-Target: DynamoDB_20120810.DescribeTable" -X POST -d '{"TableName":"mytable"}' http://localhost:12333/
// or alternately
// curl -H "X-Amz-Target: DescribeTable" -X POST -d '{"TableName":"mytable"}' http://localhost:12333/
// if you wish to just use the default API version string.
func CompatHandler(w http.ResponseWriter, req *http.Request) {
	if bbpd_runinfo.BBPDAbortIfClosed(w) {
		return
	}
	// look for the X-Amz-Target header
	target_, target_ok := req.Header[aws_const.AMZ_TARGET_HDR]
	if !target_ok {
		e := fmt.Sprintf("bbpd_route.CompatHandler:missing %s", aws_const.AMZ_TARGET_HDR)
		log.Printf(e)
		http.Error(w, e, http.StatusBadRequest)
		return
	}
	target := target_[0]
	normalized_target := target
	target_version_delim := "."
	// allow the header to have the API version string or not
	if strings.Contains(target, target_version_delim) {
		vers_target := strings.SplitN(target, target_version_delim, 2)
		if vers_target[0] != aws_const.CURRENT_API_VERSION {
			e := fmt.Sprintf("bbpd_route.CompatHandler:unsupported API version '%s'", vers_target[0])
			log.Printf(e)
			http.Error(w, e, http.StatusBadRequest)
			return
		}
		normalized_target = vers_target[1]
	}
	endpoint_path := "/" + normalized_target
	if endpoint_path == COMPATPATH || normalized_target == "" {
		e := fmt.Sprintf("bbpd_route.CompatHandler:must call named endpoint")
		log.Printf(e)
		http.Error(w, e, http.StatusBadRequest)
		return
	}

	// call the proper handler for the header
	switch endpoint_path {
	case DESCRIBETABLEPATH:
		describe_table_route.RawPostHandler(w, req)
		return
	case LISTTABLESPATH:
		list_tables_route.RawPostHandler(w, req)
		return
	case CREATETABLEPATH:
		create_table_route.RawPostHandler(w, req)
		return
	case UPDATETABLEPATH:
		update_table_route.RawPostHandler(w, req)
		return
	case STATUSTABLEPATH:
		describe_table_route.StatusTableHandler(w, req)
		return
	case PUTITEMPATH:
		put_item_route.RawPostHandler(w, req)
		return
	case GETITEMPATH:
		get_item_route.RawPostHandler(w, req)
		return
	case BATCHGETITEMPATH:
		batch_get_item_route.BatchGetItemHandler(w, req)
		return
	case BATCHWRITEITEMPATH:
		batch_write_item_route.BatchWriteItemHandler(w, req)
		return
	case DELETEITEMPATH:
		delete_item_route.RawPostHandler(w, req)
		return
	case UPDATEITEMPATH:
		update_item_route.RawPostHandler(w, req)
		return
	case QUERYPATH:
		query_route.RawPostHandler(w, req)
		return
	case SCANPATH:
		scan_route.RawPostHandler(w, req)
		return
	default:
		e := fmt.Sprintf("bbpd_route.CompatHandler:unknown endpoint '%s'", endpoint_path)
		log.Printf(e)
		http.Error(w, e, http.StatusBadRequest)
		return
	}
}
Example #11
0
// BBPD-only endpoint.
// GetItemJSONHandler issues a GetItem request to aws and then transforms the Response into
// a ResponseItemJSON.
func GetItemJSONHandler(w http.ResponseWriter, req *http.Request) {
	if bbpd_runinfo.BBPDAbortIfClosed(w) {
		return
	}
	start := time.Now()
	bodybytes, read_err := ioutil.ReadAll(req.Body)
	req.Body.Close()
	if read_err != nil && read_err != io.EOF {
		e := fmt.Sprintf("get_item_route.GetItemJSONHandler err reading req body: %s", read_err.Error())
		log.Printf(e)
		http.Error(w, e, http.StatusInternalServerError)
		return
	}

	resp_body, code, resp_err := authreq.RetryReqJSON_V4(bodybytes, get.GETITEM_ENDPOINT)

	if resp_err != nil {
		e := fmt.Sprintf("get_item_route.GetItemJSONHandler: resp err calling %s err %s (input json: %s)",
			get.GETITEM_ENDPOINT, resp_err.Error(), string(bodybytes))
		log.Printf(e)
		http.Error(w, e, http.StatusInternalServerError)
		return
	}

	if ep.HttpErr(code) {
		e := fmt.Sprintf("get_item_route.GetItemJSONHandler: http err %d calling %s (input json: %s)",
			code, get.GETITEM_ENDPOINT, string(bodybytes))
		route_response.WriteError(w, code, e, resp_body)
		return
	}

	// translate the Response to a ResponseItemJSON
	resp := get.NewResponse()
	um_err := json.Unmarshal([]byte(resp_body), resp)
	if um_err != nil {
		e := fmt.Sprintf("get_item_route.GetItemJSONHandler:err %s",
			um_err.Error())
		log.Printf(e)
		http.Error(w, e, http.StatusInternalServerError)
		return
	}
	resp_json, rerr := resp.ToResponseItemJSON()
	if rerr != nil {
		e := fmt.Sprintf("get_item_route.GetItemJSONHandler:err %s",
			rerr.Error())
		log.Printf(e)
		http.Error(w, e, http.StatusInternalServerError)
		return
	}
	json_body, jerr := json.Marshal(resp_json)
	if jerr != nil {
		e := fmt.Sprintf("get_item_route.GetItemJSONHandler:err %s",
			jerr.Error())
		log.Printf(e)
		http.Error(w, e, http.StatusInternalServerError)
		return
	}
	mr_err := route_response.MakeRouteResponse(
		w,
		req,
		json_body,
		http.StatusOK,
		start,
		get.ENDPOINT_NAME)
	if mr_err != nil {
		e := fmt.Sprintf("get_item_route.GetItemJSONHandler %s",
			mr_err.Error())
		log.Printf(e)
		http.Error(w, e, http.StatusInternalServerError)
		return
	}
}