// 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) } }
// 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) } }
// 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) } }
// 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)) }
// 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) } }
// 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) } }
// 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 } }
// 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 } }