Full Mattermost server source with integrated Community Enterprise features. Includes vendor directory for offline/air-gapped builds. Structure: - enterprise-impl/: Enterprise feature implementations - enterprise-community/: Init files that register implementations - enterprise/: Bridge imports (community_imports.go) - vendor/: All dependencies for offline builds Build (online): go build ./cmd/mattermost Build (offline/air-gapped): go build -mod=vendor ./cmd/mattermost 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
109 lines
2.4 KiB
Go
109 lines
2.4 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
package app
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
type PluginResponseWriter struct {
|
|
pipeWriter *io.PipeWriter
|
|
headers http.Header
|
|
statusCode int
|
|
ResponseReady chan struct{}
|
|
}
|
|
|
|
func NewPluginResponseWriter(pw *io.PipeWriter) *PluginResponseWriter {
|
|
return &PluginResponseWriter{
|
|
pipeWriter: pw,
|
|
headers: make(http.Header),
|
|
ResponseReady: make(chan struct{}),
|
|
}
|
|
}
|
|
|
|
func (rt *PluginResponseWriter) Header() http.Header {
|
|
if rt.headers == nil {
|
|
rt.headers = make(http.Header)
|
|
}
|
|
return rt.headers
|
|
}
|
|
|
|
// markResponseReady safely closes the ResponseReady channel if not already closed
|
|
func (rt *PluginResponseWriter) markResponseReady() {
|
|
select {
|
|
case <-rt.ResponseReady:
|
|
default:
|
|
close(rt.ResponseReady)
|
|
}
|
|
}
|
|
|
|
func (rt *PluginResponseWriter) Write(data []byte) (int, error) {
|
|
// Signal that response are ready on first write if not already done
|
|
rt.markResponseReady()
|
|
return rt.pipeWriter.Write(data)
|
|
}
|
|
|
|
func (rt *PluginResponseWriter) WriteHeader(statusCode int) {
|
|
if rt.statusCode == 0 {
|
|
rt.statusCode = statusCode
|
|
rt.markResponseReady()
|
|
}
|
|
}
|
|
|
|
func (rt *PluginResponseWriter) Flush() {
|
|
// Signal response are ready if not already done
|
|
rt.markResponseReady()
|
|
// Pipe doesn't need explicit flushing, but we implement the interface
|
|
}
|
|
|
|
// From net/http/httptest/recorder.go
|
|
func parseContentLength(cl string) int64 {
|
|
cl = strings.TrimSpace(cl)
|
|
if cl == "" {
|
|
return -1
|
|
}
|
|
n, err := strconv.ParseInt(cl, 10, 64)
|
|
if err != nil {
|
|
return -1
|
|
}
|
|
return n
|
|
}
|
|
|
|
func (rt *PluginResponseWriter) GenerateResponse(pr *io.PipeReader) *http.Response {
|
|
res := &http.Response{
|
|
Proto: "HTTP/1.1",
|
|
ProtoMajor: 1,
|
|
ProtoMinor: 1,
|
|
StatusCode: rt.statusCode,
|
|
Header: rt.headers.Clone(),
|
|
Body: pr,
|
|
}
|
|
|
|
if res.StatusCode == 0 {
|
|
res.StatusCode = http.StatusOK
|
|
}
|
|
|
|
res.Status = fmt.Sprintf("%03d %s", res.StatusCode, http.StatusText(res.StatusCode))
|
|
|
|
res.ContentLength = parseContentLength(rt.headers.Get("Content-Length"))
|
|
|
|
return res
|
|
}
|
|
|
|
func (rt *PluginResponseWriter) CloseWithError(err error) error {
|
|
// Ensure ResponseReady is closed to prevent deadlock
|
|
rt.markResponseReady()
|
|
return rt.pipeWriter.CloseWithError(err)
|
|
}
|
|
|
|
func (rt *PluginResponseWriter) Close() error {
|
|
// Ensure ResponseReady is closed to prevent deadlock
|
|
rt.markResponseReady()
|
|
return rt.pipeWriter.Close()
|
|
}
|