@@ -33,6 +33,7 @@ import { accessHeadlessLogs } from "../auth/rate-limiter";
33
33
import { BearerAuth } from "../auth/bearer-authenticator" ;
34
34
import { ProjectsService } from "../projects/projects-service" ;
35
35
import { HostContextProvider } from "../auth/host-context-provider" ;
36
+ import { TraceContext } from "@gitpod/gitpod-protocol/lib/util/tracing" ;
36
37
37
38
export const HEADLESS_LOGS_PATH_PREFIX = "/headless-logs" ;
38
39
export const HEADLESS_LOG_DOWNLOAD_PATH_PREFIX = "/headless-log-download" ;
@@ -54,79 +55,86 @@ export class HeadlessLogController {
54
55
authenticateAndAuthorize ,
55
56
asyncHandler ( async ( req : express . Request , res : express . Response ) => {
56
57
const span = opentracing . globalTracer ( ) . startSpan ( HEADLESS_LOGS_PATH_PREFIX ) ;
57
- const params = { instanceId : req . params . instanceId , terminalId : req . params . terminalId } ;
58
- const user = req . user as User ; // verified by authenticateAndAuthorize
59
-
60
- const instanceId = params . instanceId ;
61
- const ws = await this . authorizeHeadlessLogAccess ( span , user , instanceId , res ) ;
62
- if ( ! ws ) {
63
- return ;
64
- }
65
- const { workspace, instance } = ws ;
58
+ try {
59
+ const params = { instanceId : req . params . instanceId , terminalId : req . params . terminalId } ;
60
+ const user = req . user as User ; // verified by authenticateAndAuthorize
66
61
67
- const logCtx = { userId : user . id , instanceId, workspaceId : workspace ! . id } ;
68
- log . debug ( logCtx , HEADLESS_LOGS_PATH_PREFIX ) ;
62
+ const instanceId = params . instanceId ;
63
+ const ws = await this . authorizeHeadlessLogAccess ( span , user , instanceId , res ) ;
64
+ if ( ! ws ) {
65
+ return ;
66
+ }
67
+ const { workspace, instance } = ws ;
69
68
70
- const aborted = new Deferred < boolean > ( ) ;
71
- try {
72
- const head = {
73
- "Content-Type" : "text/html; charset=utf-8" , // is text/plain, but with that node.js won't stream...
74
- "Transfer-Encoding" : "chunked" ,
75
- "Cache-Control" : "no-cache, no-store, must-revalidate" , // make sure stream are not re-used on reconnect
76
- } ;
77
- res . writeHead ( 200 , head ) ;
69
+ const logCtx = { userId : user . id , instanceId, workspaceId : workspace ! . id } ;
70
+ log . debug ( logCtx , HEADLESS_LOGS_PATH_PREFIX ) ;
78
71
79
- const abort = ( err : any ) => {
80
- aborted . resolve ( true ) ;
81
- log . debug ( logCtx , "headless-log: aborted" ) ;
82
- } ;
83
- req . on ( "abort" , abort ) ; // abort handling if the reqeuest was aborted
72
+ const aborted = new Deferred < boolean > ( ) ;
73
+ try {
74
+ const head = {
75
+ "Content-Type" : "text/html; charset=utf-8" , // is text/plain, but with that node.js won't stream...
76
+ "Transfer-Encoding" : "chunked" ,
77
+ "Cache-Control" : "no-cache, no-store, must-revalidate" , // make sure stream are not re-used on reconnect
78
+ } ;
79
+ res . writeHead ( 200 , head ) ;
84
80
85
- const queue = new Queue ( ) ; // Make sure we forward in the correct order
86
- const writeToResponse = async ( chunk : string ) =>
87
- queue . enqueue (
88
- ( ) =>
89
- new Promise < void > ( async ( resolve , reject ) => {
90
- if ( aborted . isResolved && ( await aborted . promise ) ) {
91
- return ;
92
- }
81
+ const abort = ( err : any ) => {
82
+ aborted . resolve ( true ) ;
83
+ log . debug ( logCtx , "headless-log: aborted" ) ;
84
+ } ;
85
+ req . on ( "abort" , abort ) ; // abort handling if the reqeuest was aborted
93
86
94
- const done = res . write ( chunk , "utf-8" , ( err ?: Error | null ) => {
95
- if ( err ) {
96
- reject ( err ) ; // propagate write error to upstream
87
+ const queue = new Queue ( ) ; // Make sure we forward in the correct order
88
+ const writeToResponse = async ( chunk : string ) =>
89
+ queue . enqueue (
90
+ ( ) =>
91
+ new Promise < void > ( async ( resolve , reject ) => {
92
+ if ( aborted . isResolved && ( await aborted . promise ) ) {
97
93
return ;
98
94
}
99
- } ) ;
100
- // handle as per doc: https://nodejs.org/api/stream.html#stream_writable_write_chunk_encoding_callback
101
- if ( ! done ) {
102
- res . once ( "drain" , resolve ) ;
103
- } else {
104
- setImmediate ( resolve ) ;
105
- }
106
- } ) ,
95
+
96
+ const done = res . write ( chunk , "utf-8" , ( err ?: Error | null ) => {
97
+ if ( err ) {
98
+ reject ( err ) ; // propagate write error to upstream
99
+ return ;
100
+ }
101
+ } ) ;
102
+ // handle as per doc: https://nodejs.org/api/stream.html#stream_writable_write_chunk_encoding_callback
103
+ if ( ! done ) {
104
+ res . once ( "drain" , resolve ) ;
105
+ } else {
106
+ setImmediate ( resolve ) ;
107
+ }
108
+ } ) ,
109
+ ) ;
110
+ const logEndpoint = HeadlessLogEndpoint . fromWithOwnerToken ( instance ) ;
111
+ await this . headlessLogService . streamWorkspaceLogWhileRunning (
112
+ logCtx ,
113
+ logEndpoint ,
114
+ instanceId ,
115
+ params . terminalId ,
116
+ writeToResponse ,
117
+ aborted ,
107
118
) ;
108
- const logEndpoint = HeadlessLogEndpoint . fromWithOwnerToken ( instance ) ;
109
- await this . headlessLogService . streamWorkspaceLogWhileRunning (
110
- logCtx ,
111
- logEndpoint ,
112
- instanceId ,
113
- params . terminalId ,
114
- writeToResponse ,
115
- aborted ,
116
- ) ;
117
119
118
- // In an ideal world, we'd use res.addTrailers()/response.trailer here. But despite being introduced with HTTP/1.1 in 1999, trailers are not supported by popular proxies (nginx, for example).
119
- // So we resort to this hand-written solution
120
- res . write ( `\n${ HEADLESS_LOG_STREAM_STATUS_CODE } : 200` ) ;
120
+ // In an ideal world, we'd use res.addTrailers()/response.trailer here. But despite being introduced with HTTP/1.1 in 1999, trailers are not supported by popular proxies (nginx, for example).
121
+ // So we resort to this hand-written solution
122
+ res . write ( `\n${ HEADLESS_LOG_STREAM_STATUS_CODE } : 200` ) ;
121
123
122
- res . end ( ) ;
123
- } catch ( err ) {
124
- log . debug ( logCtx , "error streaming headless logs" , err ) ;
124
+ res . end ( ) ;
125
+ } catch ( err ) {
126
+ log . debug ( logCtx , "error streaming headless logs" , err ) ;
125
127
126
- res . write ( `\n${ HEADLESS_LOG_STREAM_STATUS_CODE } : 500` ) ;
127
- res . end ( ) ;
128
+ res . write ( `\n${ HEADLESS_LOG_STREAM_STATUS_CODE } : 500` ) ;
129
+ res . end ( ) ;
130
+ } finally {
131
+ aborted . resolve ( true ) ; // ensure that the promise gets resolved eventually!
132
+ }
133
+ } catch ( e ) {
134
+ TraceContext . setError ( { span } , e ) ;
135
+ throw e ;
128
136
} finally {
129
- aborted . resolve ( true ) ; // ensure that the promise gets resolved eventually!
137
+ span . finish ( ) ;
130
138
}
131
139
} ) ,
132
140
] ) ;
0 commit comments