// HTTP handler for a GET of a specific doc attachment func (h *handler) handleGetAttachment() error { docid := h.PathVar("docid") attachmentName := h.PathVar("attach") revid := h.getQuery("rev") body, err := h.db.GetRev(docid, revid, false, nil) if err != nil { return err } if body == nil { return kNotFoundError } meta, ok := db.BodyAttachments(body)[attachmentName].(map[string]interface{}) if !ok { return base.HTTPErrorf(http.StatusNotFound, "missing attachment %s", attachmentName) } digest := meta["digest"].(string) data, err := h.db.GetAttachment(db.AttachmentKey(digest)) if err != nil { return err } h.setHeader("Etag", digest) if contentType, ok := meta["content_type"].(string); ok { h.setHeader("Content-Type", contentType) } if encoding, ok := meta["encoding"].(string); ok { h.setHeader("Content-Encoding", encoding) } h.response.Write(data) return nil }
// HTTP handler for a PUT of an attachment func (h *handler) handlePutAttachment() error { docid := h.PathVar("docid") attachmentName := h.PathVar("attach") attachmentContentType := h.rq.Header.Get("Content-Type") if attachmentContentType == "" { attachmentContentType = "application/octet-stream" } revid := h.getQuery("rev") if revid == "" { revid = h.rq.Header.Get("If-Match") } attachmentData, err := h.readBody() if err != nil { return err } body, err := h.db.GetRev(docid, revid, false, nil) if err != nil && base.IsDocNotFoundError(err) { // couchdb creates empty body on attachment PUT // for non-existant doc id body = db.Body{} body["_rev"] = revid } else if err != nil { return err } else if body != nil { body["_rev"] = revid } // find attachment (if it existed) attachments := db.BodyAttachments(body) if attachments == nil { attachments = make(map[string]interface{}) } // create new attachment attachment := make(map[string]interface{}) attachment["data"] = attachmentData attachment["content_type"] = attachmentContentType //attach it attachments[attachmentName] = attachment body["_attachments"] = attachments newRev, err := h.db.Put(docid, body) if err != nil { return err } h.setHeader("Etag", newRev) h.writeJSONStatus(http.StatusCreated, db.Body{"ok": true, "id": docid, "rev": newRev}) return nil }
// HTTP handler for a GET of a document func (h *handler) handleGetDoc() error { docid := h.PathVars()["docid"] revid := h.getQuery("rev") includeRevs := h.getBoolQuery("revs") openRevs := h.getQuery("open_revs") // What attachment bodies should be included? var attachmentsSince []string = nil if h.getBoolQuery("attachments") { atts := h.getQuery("atts_since") if atts != "" { err := json.Unmarshal([]byte(atts), &attachmentsSince) if err != nil { return &base.HTTPError{http.StatusBadRequest, "bad atts_since"} } } else { attachmentsSince = []string{} } } if openRevs == "" { // Single-revision GET: value, err := h.db.GetRev(docid, revid, includeRevs, attachmentsSince) if err != nil { return err } if value == nil { return kNotFoundError } h.setHeader("Etag", value["_rev"].(string)) hasBodies := (attachmentsSince != nil && value["_attachments"] != nil) if h.requestAccepts("multipart/") && (hasBodies || !h.requestAccepts("application/json")) { return h.writeMultipart(func(writer *multipart.Writer) error { h.db.WriteMultipartDocument(value, writer) return nil }) } else { h.writeJSON(value) } } else if openRevs == "all" { return &base.HTTPError{http.StatusNotImplemented, "open_revs=all unimplemented"} // TODO } else { attachmentsSince = []string{} var revids []string err := json.Unmarshal([]byte(openRevs), &revids) if err != nil { return &base.HTTPError{http.StatusBadRequest, "bad open_revs"} } err = h.writeMultipart(func(writer *multipart.Writer) error { for _, revid := range revids { var content []byte var contentType string revBody, err := h.db.GetRev(docid, revid, includeRevs, attachmentsSince) if err == nil && len(db.BodyAttachments(revBody)) > 0 { // Write as multipart, including attachments: var buffer bytes.Buffer docWriter := multipart.NewWriter(&buffer) contentType = fmt.Sprintf("multipart/related; boundary=%q", docWriter.Boundary()) h.db.WriteMultipartDocument(revBody, docWriter) docWriter.Close() content = bytes.TrimRight(buffer.Bytes(), "\r\n") } else { // Write as JSON: contentType = "application/json" if err != nil { revBody = db.Body{"missing": revid} //TODO: More specific error contentType += `; error="true"` } content, _ = json.Marshal(revBody) } partHeaders := textproto.MIMEHeader{} partHeaders.Set("Content-Type", contentType) part, _ := writer.CreatePart(partHeaders) part.Write(content) } return nil }) return err } return nil }